Telegram Trigger n8n: логика работы и настройка Telegram-бота с нуля

Telegram Trigger n8n: настройка бота и логика работы

Telegram Trigger n8n не ловит сообщения в 9 случаях из 10 по одной причине: Telegram не может достучаться до твоего n8n по HTTPS. Ниже рабочая схема, конфиги и команды диагностики. Без воды.

Быстрый ответ
Чтобы Telegram Trigger в n8n заработал, нужно четыре вещи:
  • Публичный домен с валидным HTTPS-сертификатом на порту 443, 80, 88 или 8443.
  • Переменная WEBHOOK_URL в n8n с этим HTTPS-адресом и поддержка websocket в reverse proxy.
  • Опубликованный workflow: при публикации n8n сам регистрирует вебхук у Telegram через setWebhook.
  • Один Telegram Trigger на одного бота. Второй триггер или тестовый запуск перезаписывает вебхук первого.

Проверка за 10 секунд: метод getWebhookInfo в Bot API. Поле url должно указывать на твой n8n, поле last_error_message должно быть пустым.

Диагноз: Telegram Trigger в n8n молчит

Поднял n8n. Добавил Telegram Trigger. Пишешь боту, а в n8n тишина. Или в тесте всё ловится, а после публикации бот глухой. Знакомо?

Telegram Trigger n8n устроен проще, чем кажется. Это не магия, а обычный вебхук. Telegram получает сообщение от пользователя и делает HTTPS POST на адрес, который ему когда-то сообщили. Если адрес неправильный, сертификат кривой или по пути стоит прокси без нужных заголовков, сообщение до n8n не доедет. А n8n об этом даже не узнает.

Отсюда главное правило статьи: сначала смотри, что знает о вебхуке Telegram, потом лезь в n8n. Девяносто процентов проблем видны в одном ответе API.

Что получишь на выходе

  • Рабочего Telegram-бота на self-hosted n8n в Docker за nginx с Let’s Encrypt.
  • Понимание, как Telegram Trigger регистрирует вебхук и почему тест ломает прод.
  • Готовую логику бота: команды, inline-кнопки, ответы на callback.
  • Таблицу ошибок с причинами и командами для лечения.
  • Бэкап, мониторинг вебхука и безопасное обновление.
  • Защиту: UFW, fail2ban, SSH, ограничение вебхука по подсетям Telegram.
  • Обходные пути, если белого IP нет или Telegram недоступен из твоей сети.

Время: 40-60 минут на чистый сервер, 5 минут на диагностику уже работающего.
Что нужно: VPS с публичным IP, домен, доступ к DNS, аккаунт Telegram, базовый опыт с Docker.

Системные требования

Компонент Версия Комментарий
ОС Ubuntu 24.04 LTS или Debian 12 Команды в статье под Ubuntu 24.04
Docker Engine актуальный stable с плагином Compose v2 Команда docker compose, не docker-compose
n8n 2.42.x stable Ветка 2.x, интерфейс Publish вместо тумблера Active
PostgreSQL 16 SQLite тоже работает, но для прода бери Postgres
nginx 1.24 из репозитория Ubuntu Reverse proxy и TLS-терминация
certbot из репозитория Сертификат Let’s Encrypt
Ресурсы 2 vCPU, 2 ГБ RAM, 20 ГБ диск Минимум для n8n с Postgres и парой ботов

На момент публикации актуальна версия n8n 2.42.x в ветке stable. Релизы выходят почти каждую неделю, поэтому перед установкой проверь свежие релизы на GitHub n8n.

Порты

Порт Протокол Назначение Доступен снаружи
443 TCP HTTPS: вебхуки Telegram и интерфейс n8n через nginx Да
80 TCP Выпуск и продление сертификата, редирект на HTTPS Да
22 TCP SSH Только с IP администратора
5678 TCP n8n внутри контейнера Нет, только 127.0.0.1
5432 TCP PostgreSQL Нет, только внутри docker-сети

Архитектура

Смотри, как идёт сообщение. Пользователь пишет боту. Серверы Telegram стучатся к тебе на 443. nginx снимает TLS и отдаёт запрос n8n на локальный 5678. n8n запускает workflow, а ответ уходит обратно через api.telegram.org уже исходящим запросом.

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#ffffff',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    U["Пользователь Telegram"] --> T["Серверы Telegram"]
    T -->|HTTPS POST| N["nginx на 443"]
    N --> W["n8n на 127.0.0.1:5678"]
    W --> P["PostgreSQL"]
    W --> R["Workflow бота"]
    R -->|sendMessage| A["api.telegram.org"]
    A --> U
    style U fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style N fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style W fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style R fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d

Два направления трафика. Входящий: Telegram к тебе, нужен публичный HTTPS. Исходящий: ты к api.telegram.org, нужен просто выход в интернет. Ломается обычно входящий. Но с 2026 года в российских сетях начал ломаться и исходящий, об этом ниже отдельно.

Как работает Telegram Trigger: логика под капотом

Вот тут важно. Пока не поймёшь механику, будешь лечить симптомы.

У Telegram Bot API два способа отдать боту апдейты. Первый: polling, бот сам периодически спрашивает getUpdates. Второй: webhook, Telegram сам толкает апдейты на твой URL. Telegram Trigger в n8n работает только через webhook. Polling в нём нет.

Что происходит, когда ты публикуешь workflow:

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#f8fafc',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'noteBkgColor': '#fefce8',
    'noteTextColor': '#713f12',
    'noteBorderColor': '#fbbf24',
    'actorBkg': '#f8fafc',
    'actorBorder': '#94a3b8',
    'actorTextColor': '#1e293b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'sequence': {
    'mirrorActors': false,
    'messageAlign': 'center',
    'actorMargin': 120,
    'width': 160,
    'noteMargin': 12
  }
}}%%
sequenceDiagram
    participant N as n8n
    participant T as Telegram API
    participant U as Пользователь
    N->>T: setWebhook url, allowed_updates, secret_token
    T->>N: ok true
    U->>T: Сообщение боту
    T->>N: POST /webhook/id/webhook
    Note over N: Проверка заголовка с секретом
    N->>T: HTTP 200 сразу
    Note over N: Запуск workflow
    N->>T: sendMessage
    T->>U: Ответ бота

Разберём по шагам, где тут грабли.

Один бот, один вебхук

У бота в Telegram может быть ровно один адрес вебхука. Не два, не по одному на workflow. Один. Каждый вызов setWebhook перезаписывает предыдущий адрес.

n8n прямо пишет об этом в интерфейсе ноды: на одного бота одновременно может работать только один Telegram Trigger. Добавил второй workflow с тем же токеном и опубликовал? Первый оглох. Без ошибок, без предупреждений, просто перестал получать апдейты.

Тестовый и боевой URL

У каждой ноды-триггера в n8n два адреса. Тестовый с префиксом /webhook-test/ работает, пока ты держишь открытым режим прослушивания в редакторе. Боевой с префиксом /webhook/ работает у опубликованного workflow.

Когда жмёшь Execute workflow в редакторе, n8n регистрирует у Telegram тестовый адрес. Боевой затирается. Закончил тест, закрыл редактор, а Telegram продолжает слать апдейты на тестовый URL, который уже никто не слушает. Прод мёртв.

По факту правило простое: либо снимай workflow с публикации на время теста, либо заведи второго бота для тестов. Второй вариант лучше. Боты бесплатные, нервы нет.

Секретный токен

Bot API умеет передавать секрет в заголовке X-Telegram-Bot-Api-Secret-Token. Актуальные версии n8n генерируют этот секрет сами при регистрации вебхука и проверяют его на каждом входящем запросе. Запрос без правильного заголовка нода отбрасывает.

Отсюда ловушка: если ты вручную дёрнул setWebhook на адрес n8n без секрета, n8n будет отвергать апдейты. Не трогай вебхук руками, пока workflow опубликован. Хочешь переустановить, сними с публикации и опубликуй снова.

Мгновенный ответ и повторы

Telegram ждёт от вебхука ответ 2xx. Получил ошибку или таймаут, повторяет доставку. Неполученные апдейты Telegram хранит до 24 часов, потом выбрасывает.

Telegram Trigger отвечает 200 сразу при получении, ещё до выполнения workflow. Поэтому даже тяжёлый workflow с запросом к GPT на минуту не вызовет повторной доставки. Но если n8n лежит, апдейты копятся у Telegram. Поднялся n8n, и вся очередь прилетает разом. Отсюда «бот вдруг ответил на 40 старых сообщений».

Типы апдейтов

В поле Trigger On ты выбираешь, какие события слушать. n8n передаёт этот список в allowed_updates. Вариант со звёздочкой означает все типы, кроме chat_member, message_reaction и message_reaction_count: Telegram их по умолчанию не шлёт, потому что их очень много.

Тип апдейта Когда приходит Chat ID в выражении n8n Полезные данные
message Любое новое сообщение: текст, фото, файл, стикер {{ $json.message.chat.id }} {{ $json.message.text }}
edited_message Пользователь отредактировал сообщение {{ $json.edited_message.chat.id }} {{ $json.edited_message.text }}
callback_query Нажата inline-кнопка {{ $json.callback_query.message.chat.id }} {{ $json.callback_query.data }}
channel_post Пост в канале, где бот админ {{ $json.channel_post.chat.id }} {{ $json.channel_post.text }}
inline_query Пользователь набрал @бот запрос в любом чате чата нет {{ $json.inline_query.query }}
chat_member Изменился статус участника, бот должен быть админом {{ $json.chat_member.chat.id }} {{ $json.chat_member.new_chat_member.status }}
my_chat_member Бота добавили, удалили или заблокировали {{ $json.my_chat_member.chat.id }} {{ $json.my_chat_member.new_chat_member.status }}

Выбирай только то, что реально обрабатываешь. Каждый лишний тип это лишние запуски workflow и мусор в списке выполнений.

Причины, почему бот не получает сообщения

Всё не так плохо, как ты думаешь. Всё намного проще: причин всего семь, и каждая видна в логах или в getWebhookInfo.

  1. Нет HTTPS или n8n не знает свой публичный адрес. Без WEBHOOK_URL n8n регистрирует вебхук на http://localhost:5678. Telegram такой адрес не примет и вернёт ошибку bad webhook.
  2. Невалидный сертификат. Самоподписанный без загрузки в setWebhook, просроченный или без промежуточной цепочки. Telegram не сможет установить TLS и будет копить апдейты.
  3. Неподдерживаемый порт. Вебхуки Telegram работают только на 443, 80, 88 и 8443. Порт 5678 или 9443 не пройдёт.
  4. Reverse proxy без websocket. Редактор n8n получает события через websocket. Без заголовков Upgrade тестовый запуск висит в Waiting for trigger event, даже если апдейт дошёл.
  5. Конфликт вебхуков. Второй workflow с тем же ботом, тестовый запуск поверх боевого, сторонний скрипт с тем же токеном.
  6. Режим приватности в группах. По умолчанию бот в группе видит только команды и ответы на свои сообщения. Обычный текст до него не доходит.
  7. Сеть между Telegram и сервером. Фаервол режет подсети Telegram, хостер блокирует входящие или сервер в сети, где Telegram ограничен.
Telegram и российские сети
С февраля 2026 года Роскомнадзор поэтапно ограничивает Telegram в России, а с марта 2026 из многих российских сетей сервис практически недоступен. Вебхук-интеграции на российских IP работают нестабильно в обе стороны: Telegram не всегда достучится до твоего сервера, а сервер не всегда достучится до api.telegram.org. Если бот критичен для работы, размести n8n или хотя бы точку входа вебхука на зарубежном VPS. Варианты разобраны в разделе про альтернативы.

Рецепт: настройка Telegram-бота в n8n с нуля

Схема такая: сервер, Docker, n8n с Postgres, nginx с сертификатом, бот в BotFather, workflow. Идём по порядку, каждый шаг проверяем, прежде чем переходить к следующему.

Подготовка

Перед стартом проверь три вещи. Домен, например n8n.example.ru, смотрит A-записью на IP сервера. На сервере открыты 80 и 443. У тебя есть пользователь с sudo, не root.

Где: твой рабочий компьютер или любая машина с dig.


dig +short n8n.example.ru A

Результат: IP твоего сервера. Пусто или чужой адрес, значит DNS ещё не обновился. Подожди TTL, Let’s Encrypt без правильной A-записи сертификат не выдаст.

Где: сервер n8n, пользователь с sudo.


sudo apt update && sudo apt -y upgrade
sudo apt -y install ca-certificates curl jq nginx certbot ufw fail2ban
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"
newgrp docker
docker compose version

Результат: последняя команда печатает версию Docker Compose v2. Если ругается на unknown command, плагин compose не встал, смотри официальную инструкцию Docker для Ubuntu.

Почему скрипт get.docker.com, а не пакет docker.io из Ubuntu: в репозитории дистрибутива Docker отстаёт на несколько версий и часто идёт без compose-плагина. Для прода в закрытом контуре лучше подключи репозиторий Docker вручную, как в той же инструкции.

Шаг 1. Создай бота в BotFather

Открой в Telegram @BotFather. Отправь команду /newbot. Задай отображаемое имя и username, который заканчивается на bot. В ответ придёт токен: число, двоеточие и длинная строка из букв и цифр.

Токен бота это пароль
Кто знает токен, тот управляет ботом: читает апдейты, шлёт сообщения от его имени, переставляет вебхук. Не коммить токен в git, не кидай в общие чаты, не вставляй в скриншоты. Утёк, сразу выпусти новый через команду /revoke в BotFather. Старый перестанет работать мгновенно.

Сразу реши вопрос с группами. Если бот должен читать обычные сообщения в группе, отключи режим приватности: в BotFather команда /setprivacy, выбери бота, нажми Disable. Потом удали бота из группы и добавь заново. Без переподключения изменение не применится, на этом спотыкаются все.

Для тестов сразу заведи второго бота, например mycompany_test_bot. Зачем, объясню в разделе осложнений. Коротко: чтобы тест не ломал прод.

Где: сервер n8n. Подставь свой токен вместо TOKEN.


curl -s "https://api.telegram.org/botTOKEN/getMe" | jq

Результат: JSON с ok: true и данными бота. Если curl висит до таймаута, сервер не достаёт до api.telegram.org. Дальше идти бессмысленно, сначала реши сеть.

Шаг 2. Структура каталогов и секреты

Держим всё в /opt/n8n. Один каталог, один compose-файл, один .env. Через год ты скажешь себе спасибо.

Где: сервер n8n.


sudo mkdir -p /opt/n8n/backup
sudo chown -R "$USER":"$USER" /opt/n8n
cd /opt/n8n
openssl rand -hex 32
openssl rand -hex 24
openssl rand -hex 24

Результат: три случайные строки. Первая пойдёт в ключ шифрования n8n, две другие в пароли Postgres.

N8N_ENCRYPTION_KEY нельзя потерять
Этим ключом n8n шифрует все credentials в базе, включая токен бота. Потеряешь ключ, и бэкап базы превратится в бесполезный набор байт: workflow восстановятся, а все учётки придётся заводить заново. Сохрани ключ в менеджер паролей сразу, до первого запуска.

Создай файл /opt/n8n/.env. Подставь свой домен и сгенерированные строки.


N8N_VERSION=2.42.7
N8N_HOST=n8n.example.ru
WEBHOOK_URL=https://n8n.example.ru/
N8N_ENCRYPTION_KEY=вставь_первую_строку
GENERIC_TIMEZONE=Europe/Moscow
TZ=Europe/Moscow

POSTGRES_USER=pgadmin
POSTGRES_PASSWORD=вставь_вторую_строку
POSTGRES_DB=n8n
POSTGRES_NON_ROOT_USER=n8n
POSTGRES_NON_ROOT_PASSWORD=вставь_третью_строку

Где: сервер n8n.


chmod 600 /opt/n8n/.env

Почему версия прибита гвоздями, а не latest: latest в продакшне это лотерея. Ночью перезапустился контейнер, подтянулась новая минорная версия с миграцией базы, утром бот лежит. Обновляться будем осознанно, процедура в конце статьи.

Шаг 3. Отдельный пользователь базы

n8n не должен ходить в Postgres суперпользователем. Скрипт ниже выполнится один раз при первой инициализации базы и создаст пользователя n8n с правами только на свою БД.

Создай файл /opt/n8n/init-data.sh:


#!/bin/bash
set -e
if [ -n "${POSTGRES_NON_ROOT_USER:-}" ] && [ -n "${POSTGRES_NON_ROOT_PASSWORD:-}" ]; then
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<EOSQL
CREATE USER ${POSTGRES_NON_ROOT_USER} WITH PASSWORD '${POSTGRES_NON_ROOT_PASSWORD}';
GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_NON_ROOT_USER};
GRANT CREATE ON SCHEMA public TO ${POSTGRES_NON_ROOT_USER};
EOSQL
else
echo "SETUP INFO: No non-root user credentials provided"
fi

Где: сервер n8n.


chmod +x /opt/n8n/init-data.sh

Шаг 4. docker-compose.yml

Создай /opt/n8n/docker-compose.yml. Обрати внимание на строку ports: n8n слушает только 127.0.0.1. Снаружи к нему можно попасть исключительно через nginx.


services:
  postgres:
    image: postgres:16
    restart: unless-stopped
    environment:
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_NON_ROOT_USER=${POSTGRES_NON_ROOT_USER}
      - POSTGRES_NON_ROOT_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
    volumes:
      - db_storage:/var/lib/postgresql/data
      - ./init-data.sh:/docker-entrypoint-initdb.d/init-data.sh:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 5s
      timeout: 5s
      retries: 10

  n8n:
    image: docker.n8n.io/n8nio/n8n:${N8N_VERSION}
    restart: unless-stopped
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_NON_ROOT_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_NON_ROOT_PASSWORD}
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=${WEBHOOK_URL}
      - N8N_PROXY_HOPS=1
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
      - TZ=${TZ}
    ports:
      - "127.0.0.1:5678:5678"
    volumes:
      - n8n_storage:/home/node/.n8n
    depends_on:
      postgres:
        condition: service_healthy

volumes:
  db_storage:
  n8n_storage:

Три переменные решают судьбу Telegram Trigger. WEBHOOK_URL говорит n8n, какой адрес отдавать Telegram в setWebhook. N8N_PROTOCOL=https нужен для корректных ссылок в интерфейсе. N8N_PROXY_HOPS=1 заставляет n8n доверять заголовку X-Forwarded-For от одного прокси перед ним. Полный список переменных в документации n8n по deployment-переменным.

Где: сервер n8n, каталог /opt/n8n.


cd /opt/n8n
docker compose up -d
docker compose ps
docker compose logs n8n --tail 30
curl -s http://127.0.0.1:5678/healthz

Результат: оба контейнера в статусе running, postgres в состоянии healthy. В логах n8n строка про готовность редактора. Последний curl возвращает {«status»:»ok»}.

Шаг 5. nginx и сертификат

Сначала поднимаем голый HTTP, чтобы certbot мог пройти проверку домена. Потом добавляем HTTPS. В два захода, зато без сюрпризов и без того, чтобы certbot сам переписывал тебе конфиги.

Создай /etc/nginx/sites-available/n8n.conf с временным содержимым:


server {
    listen 80;
    server_name n8n.example.ru;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

Где: сервер n8n.


sudo ln -s /etc/nginx/sites-available/n8n.conf /etc/nginx/sites-enabled/n8n.conf
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx
sudo certbot certonly --webroot -w /var/www/html -d n8n.example.ru --agree-tos -m admin@example.ru --no-eff-email

Результат: certbot пишет Successfully received certificate и путь к /etc/letsencrypt/live/n8n.example.ru/. Ошибка про unauthorized или connection refused почти всегда значит, что A-запись не на этот сервер или порт 80 закрыт.

Теперь полный конфиг. Перезапиши /etc/nginx/sites-available/n8n.conf:


map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

server {
    listen 80;
    server_name n8n.example.ru;

    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

server {
    listen 443 ssl http2;
    server_name n8n.example.ru;

    ssl_certificate     /etc/letsencrypt/live/n8n.example.ru/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/n8n.example.ru/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;

    client_max_body_size 50m;

    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
        proxy_read_timeout 300s;
    }
}

Не перепутай: в ssl_certificate указывай fullchain.pem, а не cert.pem. С cert.pem браузер может открыть сайт, потому что сам докачает промежуточный сертификат. Telegram докачивать не будет и отвалится с ошибкой проверки сертификата.

Строки с Upgrade и Connection это и есть поддержка websocket. Без них редактор n8n не получит событие о пришедшем апдейте, и тестовый запуск будет вечно ждать.

Где: сервер n8n.


sudo nginx -t && sudo systemctl reload nginx
curl -sI https://n8n.example.ru/healthz | head -n 1
sudo systemctl list-timers | grep certbot

Результат: nginx -t пишет syntax is ok, curl возвращает HTTP/2 200, таймер certbot.timer в списке. Таймер продлевает сертификат автоматически. Чтобы nginx подхватывал новый сертификат, добавь хук перезагрузки:


echo 'renew_hook = systemctl reload nginx' | sudo tee -a /etc/letsencrypt/renewal/n8n.example.ru.conf
sudo certbot renew --dry-run

Где: сервер n8n. Результат: dry-run заканчивается строкой Congratulations, all simulated renewals succeeded.

Шаг 6. Credentials в n8n

Открой https://n8n.example.ru, создай учётку владельца. Дальше:

  1. Меню слева, раздел Credentials, кнопка Create.
  2. Найди Telegram API.
  3. Вставь токен в поле Access Token.
  4. Сохрани. n8n сразу проверит подключение и покажет Connection tested successfully.

Заведи две учётки: «TG prod bot» и «TG test bot». Названия скучные, зато через полгода не перепутаешь. Подробности в документации n8n по Telegram credentials.

Шаг 7. Логика бота: роутер вместо десяти триггеров

Раз триггер на бота может быть только один, вся логика должна жить за ним. Правильный паттерн: один workflow-роутер с Telegram Trigger, который раскидывает апдейты по под-workflow через ноду Execute Workflow.

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#ffffff',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    T["Telegram Trigger"] --> S["Switch по типу апдейта"]
    S --> C["Команды /start /help"]
    S --> B["Нажатия кнопок"]
    S --> F["Файлы и фото"]
    S --> X["Всё остальное"]
    C --> W1["Под-workflow: команды"]
    B --> W2["Под-workflow: кнопки"]
    F --> W3["Под-workflow: файлы"]
    X --> L["Ответ: не понял"]
    style T fill:#f8fafc,stroke:#3b82f6,stroke-width:2px,color:#1e40af
    style S fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style L fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412

Плюсы роутера: каждый сценарий живёт отдельно и правится без риска сломать соседей. Тестировать под-workflow можно с фиксированными данными, не трогая вебхук вообще.

Настройка Telegram Trigger

Параметр Значение Зачем
Credential TG prod bot В тестовой копии workflow ставь TG test bot
Trigger On Message, Callback Query Только то, что обрабатываешь
Download Images/Files Выключено Включай, только если реально обрабатываешь вложения
Restrict to User IDs ID через запятую Для внутренних ботов: чужие апдейты отсекаются на входе

Restrict to User IDs недооценённая опция. Для служебного бота, которым пользуются пять админов, это бесплатный слой защиты. Свой user ID увидишь в первом же выполнении, в поле message.from.id.

Switch: раскладка по типам

Добавь после триггера ноду Switch в режиме Rules. Правила по порядку, срабатывает первое совпавшее:

Выход Условие Выражение
Кнопки exists {{ $json.callback_query }}
Команды String starts with / {{ $json.message.text }}
Файлы exists {{ $json.message.document ?? $json.message.photo }}
Fallback Включи опцию Fallback Output все прочие апдейты

Для команд внутри под-workflow сделай второй Switch по точному значению текста. Учти, что в группах команда приходит как /start@имя_бота. Нормализуй её выражением:


{{ $json.message.text.split(' ')[0].split('@')[0].toLowerCase() }}

Результат: /start@mycompany_bot превращается в /start, а /Help argument превращается в /help.

Ответ с inline-кнопками

Нода Telegram, Resource: Message, Operation: Send Message. Поля:

  • Chat ID: {{ $json.message.chat.id }}
  • Text: текст ответа
  • Reply Markup: Inline Keyboard, кнопки с Callback Data вида approve_42 и reject_42
  • Additional Fields, Parse Mode: HTML, если нужна разметка

Callback Data ограничен 64 байтами. Кириллица занимает по два байта на символ. Клади туда короткие идентификаторы, а не JSON на полэкрана.

Обработка нажатия кнопки

Когда пользователь жмёт inline-кнопку, Telegram показывает на ней часики, пока бот не подтвердит нажатие. Не подтвердишь, часики повисят и пропадут, а пользователь решит, что бот сломался.

Первой нодой в под-workflow кнопок ставь Telegram, Resource: Callback, Operation: Answer Query. Query ID: {{ $json.callback_query.id }}. Можно добавить короткий текст всплывашки. Потом уже логика.

Разбор данных кнопки удобнее сделать нодой Code:


const cq = $input.first().json.callback_query;
const [action, id] = cq.data.split('_');
return [{
  json: {
    action,
    id,
    chatId: cq.message.chat.id,
    messageId: cq.message.message_id,
    userId: cq.from.id,
    userName: cq.from.username || cq.from.first_name
  }
}];

Результат: на выходе чистые поля action, id, chatId. Дальше Switch по action и нода Telegram с операцией Edit Message Text, чтобы заменить кнопки на «Одобрено пользователем такой-то». Так никто не нажмёт одну кнопку дважды.

Лимиты отправки

Telegram ограничивает ботов примерно 30 сообщениями в секунду суммарно и одним сообщением в секунду в один чат. В группах лимит ещё ниже, около 20 сообщений в минуту. Рассылаешь уведомления пачкой, ставь Loop Over Items и Wait на секунду между отправками. Иначе поймаешь ошибку 429 Too Many Requests с полем retry_after.

Шаг 8. Тест на тестовом боте и публикация

Порядок, который не ломает прод:

  1. Скачай workflow через меню Download, импортируй копию, назови её с суффиксом TEST.
  2. В копии поменяй credential триггера и всех Telegram-нод на TG test bot.
  3. Нажми Execute workflow и напиши тестовому боту. Выполнение должно появиться сразу.
  4. Отладил, перенеси изменения в боевой workflow и нажми Publish.

После Publish n8n вызывает setWebhook с боевым адресом. Проверяем, что Telegram с этим согласен.

Где: сервер n8n.


curl -s "https://api.telegram.org/botTOKEN/getWebhookInfo" | jq

Результат: примерно такой ответ.


{
  "ok": true,
  "result": {
    "url": "https://n8n.example.ru/webhook/4f1c2b7e-0d8a-4b4e-9a51-2f6c9d3e8a10/webhook",
    "has_custom_certificate": false,
    "pending_update_count": 0,
    "max_connections": 40,
    "ip_address": "203.0.113.25",
    "allowed_updates": ["message", "callback_query"]
  }
}

url начинается с /webhook/, а не с /webhook-test/. pending_update_count ноль. Полей last_error_date и last_error_message нет. Это здоровый бот.

Проверка: убедись, что всё работает

Проверяем снизу вверх: сервисы, сеть, вебхук, выполнение. Если какой-то уровень красный, выше не смотри.

Что проверяем Команда Норма
Контейнеры docker compose ps running, postgres healthy
n8n жив curl -s http://127.0.0.1:5678/healthz {«status»:»ok»}
HTTPS снаружи curl -sI https://n8n.example.ru/healthz HTTP/2 200
Исходящий к Telegram curl -s api.telegram.org/botTOKEN/getMe ok: true
Вебхук getWebhookInfo боевой url, ноль ошибок
Запросы доходят grep webhook в access.log nginx POST с кодом 200 от подсетей Telegram

Где: сервер n8n.


cd /opt/n8n
docker compose ps
curl -s http://127.0.0.1:5678/healthz; echo
curl -sI https://n8n.example.ru/healthz | head -n 1
sudo tail -f /var/log/nginx/access.log | grep --line-buffered '/webhook/'

Оставь tail открытым и напиши боту. Должна появиться строка с POST, кодом 200 и IP из диапазонов 149.154.160.0/20 или 91.108.4.0/22. Строки нет совсем, значит Telegram до nginx не доходит: DNS, фаервол, порт или сертификат. Строка с 4xx или 5xx, значит дошёл, но nginx или n8n ответили ошибкой.

Проверка сертификата глазами Telegram:

Где: любая внешняя машина, не сам сервер.


echo | openssl s_client -connect n8n.example.ru:443 -servername n8n.example.ru 2>/dev/null | openssl x509 -noout -subject -issuer -dates
echo | openssl s_client -connect n8n.example.ru:443 -servername n8n.example.ru 2>/dev/null | grep -E 'Verify return code'

Результат: Verify return code: 0 (ok) и дата notAfter в будущем. Код 21 unable to verify the first certificate означает, что в nginx указан cert.pem вместо fullchain.pem.

Логи n8n при проблемах с выполнением:

Где: сервер n8n, каталог /opt/n8n.


docker compose logs n8n --since 15m | grep -iE 'error|telegram|webhook'

И последний уровень: в интерфейсе n8n раздел Executions. Каждый апдейт, который прошёл триггер, оставляет там запись. Записи есть, а ответа нет, значит проблема в нодах после триггера, а не во вебхуке.

Осложнения: ошибки Telegram Trigger и как их лечить

«Само заработало» в моём словаре означает «само сломается в пятницу вечером». Поэтому лечим по алгоритму, а не перезапусками.

%%{init: {
  'theme': 'base',
  'themeVariables': {
    'primaryColor': '#ffffff',
    'primaryTextColor': '#1e293b',
    'primaryBorderColor': '#94a3b8',
    'lineColor': '#64748b',
    'fontSize': '15px',
    'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
  },
  'flowchart': {'curve': 'linear', 'nodeSpacing': 50, 'rankSpacing': 50}
}}%%
flowchart TD
    A["Бот молчит"] --> B["getWebhookInfo"]
    B --> C{"url пустой?"}
    C -->|да| D["Workflow не опубликован"]
    C -->|нет| E{"url с webhook-test?"}
    E -->|да| F["Тест затёр прод: переопубликуй"]
    E -->|нет| G{"Есть last_error_message?"}
    G -->|да| H["Смотри таблицу ошибок"]
    G -->|нет| I{"Есть запись в Executions?"}
    I -->|нет| J["Тип апдейта или Restrict to User IDs"]
    I -->|да| K["Ошибка в нодах после триггера"]
    style A fill:#f8fafc,stroke:#f97316,stroke-width:2px,color:#9a3412
    style D fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style F fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style H fill:#f8fafc,stroke:#ef4444,stroke-width:2px,color:#b91c1c
    style J fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
    style K fill:#f8fafc,stroke:#22c55e,stroke-width:2px,color:#15803d
Ошибка Причина Решение
Bad Request: bad webhook: An HTTPS URL must be provided for webhook WEBHOOK_URL не задан или задан с http Задай WEBHOOK_URL=https://домен/ и пересоздай контейнер
Waiting for trigger event висит вечно Нет websocket в reverse proxy или тест затёр вебхук другим ботом Добавь заголовки Upgrade и Connection в nginx
Работает только в тесте или только в проде Один вебхук на бота, тест и прод перезаписывают друг друга Отдельный тестовый бот или снимай с публикации на время теста
SSL error: certificate verify failed Неполная цепочка, просрочка, самоподписанный сертификат fullchain.pem в nginx, проверь renew
Wrong response from the webhook: 502 Bad Gateway n8n лежит или nginx смотрит не на тот порт docker compose ps, проверь proxy_pass
Wrong response from the webhook: 403 Forbidden Вебхук ставили вручную без секрета или режет allow/deny nginx Переопубликуй workflow, проверь правила allow
Wrong response from the webhook: 404 Not Found Workflow удалён или снят с публикации, а вебхук остался Опубликуй workflow или удали вебхук через deleteWebhook
Connection timed out в last_error_message Порт закрыт, хостер режет входящие, сеть блокирует Telegram Проверь UFW и фаервол хостера, рассмотри зарубежный VPS
409 Conflict: terminated by other getUpdates request Кто-то параллельно опрашивает бота через polling Найди и останови второй процесс с тем же токеном
401 Unauthorized Токен отозван или вставлен с пробелом Перевыпусти токен в BotFather, обнови credential
Бот в группе не видит обычные сообщения Включён privacy mode /setprivacy Disable и переподключи бота к группе
Бот ответил на десятки старых сообщений n8n лежал, Telegram накопил очередь Переустанови вебхук с drop_pending_updates

Бот работает только в тесте или только в проде

Причина: один вебхук на бота. Последний, кто вызвал setWebhook, получает все апдейты.

Решение: закрой вкладку с тестовым запуском. Сними workflow с публикации и опубликуй снова: n8n заново зарегистрирует боевой адрес. Проверь getWebhookInfo, в url должен быть /webhook/ без -test.

Где: сервер n8n.


curl -s "https://api.telegram.org/botTOKEN/getWebhookInfo" | jq -r '.result.url'

Waiting for trigger event и тишина

Причина: апдейт пришёл в n8n, но редактор не узнал об этом, потому что websocket не проксируется. Вторая причина реже: ты слушаешь тестовым запуском, а пишешь другому боту.

Решение: проверь, что в location есть proxy_http_version 1.1 и заголовки Upgrade и Connection. Если перед nginx стоит ещё один прокси, например на роутере или в Cloudflare, websocket должен пропускать и он.

Где: сервер n8n.


sudo nginx -T 2>/dev/null | grep -nE 'proxy_http_version|Upgrade|connection_upgrade'

Результат: три-четыре строки с этими директивами. Пусто, значит конфиг не тот или не подключён в sites-enabled.

Ошибка сертификата

Причина: чаще всего cert.pem вместо fullchain.pem. Реже сертификат протух, потому что certbot продлил, а nginx не перечитал.

Решение: поправь путь, перезагрузи nginx и сбрось ошибку, переопубликовав workflow.

Где: сервер n8n.


sudo grep -n 'ssl_certificate ' /etc/nginx/sites-available/n8n.conf
sudo certbot certificates
sudo nginx -t && sudo systemctl reload nginx

Очередь старых апдейтов

Причина: n8n был недоступен, Telegram копил апдейты до 24 часов. После подъёма вся пачка прилетает разом.

Решение: если старые сообщения не нужны, сбрось очередь. Сначала сними workflow с публикации, потом удали вебхук с флагом drop_pending_updates, потом опубликуй снова.

Сброс очереди необратим
Флаг drop_pending_updates удаляет все недоставленные апдейты без возможности вернуть. Если в очереди могли быть заявки, нажатия кнопок согласования или файлы от пользователей, они пропадут молча. Сначала посмотри pending_update_count и реши, нужна ли тебе эта очередь.

Где: сервер n8n. Workflow уже снят с публикации.


curl -s "https://api.telegram.org/botTOKEN/getWebhookInfo" | jq '.result.pending_update_count'
curl -s "https://api.telegram.org/botTOKEN/deleteWebhook?drop_pending_updates=true" | jq

Результат: ok: true, description Webhook was deleted. После этого нажми Publish, n8n зарегистрирует вебхук заново с пустой очередью.

409 Conflict от getUpdates

Причина: где-то крутится старый скрипт на aiogram или python-telegram-bot с тем же токеном. Он вызывает deleteWebhook при старте и уходит в polling. Вебхук n8n тихо умирает. Ищется это долго, потому что скрипт обычно запущен в screen на сервере, про который все забыли.

Решение: самый быстрый способ найти виновника это перевыпустить токен. Старый процесс отвалится с 401, и кто-то обязательно придёт жаловаться. Заодно узнаешь, где он живёт.

Бот молчит в группе

Причина: privacy mode. Бот получает только команды, упоминания и ответы на свои сообщения.

Решение: /setprivacy в BotFather, Disable, потом удали бота из группы и добавь заново. Альтернатива: сделай бота админом группы, админы видят всё и без отключения privacy mode.

Откат, если всё сломал

Бывает. Порядок отката: вебхук, контейнеры, данные. Workflow и credentials живут в Postgres, поэтому откат контейнера на старую версию без отката базы не всегда помогает. Подробнее в разделе про обновление.

Где: сервер n8n, каталог /opt/n8n.


curl -s "https://api.telegram.org/botTOKEN/deleteWebhook" | jq
docker compose down
sed -i 's/^N8N_VERSION=.*/N8N_VERSION=ПРЕДЫДУЩАЯ_ВЕРСИЯ/' .env
docker compose up -d

Удаление вебхука на время отката нужно, чтобы Telegram не долбился в лежащий сервер и не копил ошибки. Апдейты при этом будут ждать в очереди до 24 часов, после публикации workflow они придут.

Альтернативы Telegram Trigger

Telegram Trigger не единственный путь. Иногда другой вариант честно лучше. Смотри по ситуации.

Вариант Когда подходит Минусы
Telegram Trigger, основной вариант Есть публичный HTTPS, один бот на один workflow-роутер Один триггер на бота, нужен белый адрес
Webhook-нода и ручной setWebhook Нужен полный контроль: свой путь, свой секрет, свой список апдейтов Проверку секрета пишешь сам
Schedule и getUpdates через HTTP Request Нет публичного адреса, задержка в минуту допустима Задержка, лишние выполнения, ручной учёт offset
Cloudflare Tunnel или аналог n8n дома или в офисе без белого IP Зависимость от внешнего сервиса, нужна поддержка websocket
Прокси-VPS за рубежом n8n в российской сети, Telegram оттуда нестабилен Ещё один сервер в обслуживании
Отдельный бот на aiogram Сложный диалог, состояния, высокая нагрузка Код, деплой и поддержка вместо визуального workflow
Только Telegram node без триггера Бот лишь шлёт уведомления и ничего не принимает Не умеет принимать команды

Webhook-нода с ручной регистрацией

Обычная нода Webhook в n8n принимает любой POST. Ты сам вызываешь setWebhook на её боевой URL и сам задаёшь secret_token. Плюс подхода: путь предсказуемый, например /webhook/tg-prod, и его легко закрыть в nginx по подсетям Telegram.

Где: сервер n8n. SECRET это строка из букв, цифр, дефиса и подчёркивания длиной до 256 символов.


curl -s "https://api.telegram.org/botTOKEN/setWebhook" \
  -d "url=https://n8n.example.ru/webhook/tg-prod" \
  -d "secret_token=SECRET" \
  -d 'allowed_updates=["message","callback_query"]' | jq

В workflow после Webhook-ноды поставь IF: заголовок {{ $json.headers[‘x-telegram-bot-api-secret-token’] }} равен твоему секрету. Не равен, ветка в никуда. Webhook-ноду настрой на Respond Immediately, чтобы Telegram получал 200 сразу. Подробности по методам в документации Bot API.

Polling по расписанию

Нода Schedule раз в минуту, затем HTTP Request на getUpdates с параметром offset, равным последнему update_id плюс один. offset храни в Data Table или в Postgres. Работает без публичного адреса вообще. Минус очевиден: бот отвечает с задержкой до минуты, и каждую минуту крутится выполнение. Для бота уведомлений с кнопкой «подтвердить» терпимо, для диалога нет.

n8n в российской сети

Если сервер с n8n должен остаться в РФ, разделяй трафик. Входящий: nginx на зарубежном VPS принимает вебхук и проксирует его к n8n через VPN-туннель. Исходящий: n8n ходит к api.telegram.org через HTTP-прокси на том же VPS. Для исходящего у n8n есть штатные переменные HTTPS_PROXY и NO_PROXY.

Добавь в environment сервиса n8n в docker-compose.yml:


      - HTTPS_PROXY=http://10.10.0.1:3128
      - NO_PROXY=localhost,127.0.0.1,postgres

NO_PROXY не забудь. Без него n8n попробует ходить в собственную базу через прокси, и ты получишь очень интересные ошибки. Учти, что прокси будет применяться ко всем исходящим HTTPS-запросам нод, не только к Telegram.

Почему основной вариант всё же Telegram Trigger

Он регистрирует вебхук сам, сам генерирует и проверяет секрет, сам переустанавливает адрес при публикации. Меньше ручных шагов, меньше мест для ошибки. Ручная Webhook-нода оправдана, когда нужна жёсткая фильтрация по пути в nginx или несколько независимых окружений на одном инстансе.

Профилактика: безопасность, бэкап, мониторинг

Капля никотина убивает лошадь. Одна забытая строка allow all в nginx открывает редактор n8n со всеми токенами всему интернету. Закрываем по слоям.

UFW

Docker и UFW
Docker пишет свои правила iptables в обход UFW. Порт, опубликованный как 5678:5678 без адреса, будет доступен из интернета, даже если UFW его запрещает. Поэтому в docker-compose.yml порт n8n привязан к 127.0.0.1. Не убирай эту привязку.

Где: сервер n8n. Вместо 203.0.113.10 подставь свой IP администратора. Не потеряй SSH-сессию: держи открытой вторую вкладку до проверки.


sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Результат: Status active, разрешены 22 только с твоего IP, 80 и 443 со всех.

Ограничение вебхука по подсетям Telegram

Telegram шлёт вебхуки с подсетей 149.154.160.0/20 и 91.108.4.0/22. Актуальный список держи сверенным со страницей Telegram о вебхуках. Путь Telegram Trigger имеет предсказуемую форму /webhook/UUID/webhook, его можно поймать регуляркой.

Добавь в server на 443 перед location / такой блок:


    location ~ ^/webhook(-test)?/[0-9a-f-]+/webhook$ {
        allow 149.154.160.0/20;
        allow 91.108.4.0/22;
        deny all;

        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

Не перепутай: такой же путь вида /webhook/UUID/webhook используют и некоторые другие ноды-триггеры n8n. Если у тебя на инстансе есть триггеры GitHub или других сервисов, их запросы с этим правилом получат 403. Тогда либо перечисли их подсети, либо переходи на Webhook-ноду с собственным путём.

Редактор n8n тоже стоит закрыть, если им пользуются только из офиса или через VPN. В location / добавь allow для офисных адресов и deny all. Вебхуки Telegram при этом продолжат ходить через регулярный location выше.

Где: сервер n8n.


sudo nginx -t && sudo systemctl reload nginx
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://n8n.example.ru/webhook/00000000-0000-0000-0000-000000000000/webhook

Результат: с сервера или твоего ПК curl вернёт 403. Значит, правило работает. Потом напиши боту и убедись, что он отвечает.

SSH hardening

Создай /etc/ssh/sshd_config.d/10-hardening.conf. Имя пользователя deploy замени на своё. Перед этим убедись, что вход по ключу работает.


PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
LoginGraceTime 30
AllowUsers deploy
X11Forwarding no

Где: сервер n8n, в открытой второй SSH-сессии.


sudo sshd -t && sudo systemctl reload ssh

Результат: sshd -t молчит, значит конфиг валиден. Не закрывая текущую сессию, открой новую и проверь вход.

fail2ban

Создай /etc/fail2ban/jail.local:


[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = 127.0.0.1/8 ::1 203.0.113.10

[sshd]
enabled = true
backend = systemd

[nginx-botsearch]
enabled = true
port = http,https
logpath = /var/log/nginx/access.log

Где: сервер n8n.


sudo systemctl enable --now fail2ban
sudo systemctl restart fail2ban
sudo fail2ban-client status

Результат: в списке jail видны sshd и nginx-botsearch. Свой IP в ignoreip обязателен, иначе однажды забанишь сам себя после пяти опечаток в пароле от sudo.

Мониторинг вебхука

Самая неприятная поломка бота тихая: всё зелёное, контейнеры работают, а вебхук давно указывает в никуда. Ловим это скриптом, который раз в пять минут смотрит getWebhookInfo.

Сначала сохрани токен в отдельный файл с закрытыми правами.

Где: сервер n8n.


sudo mkdir -p /etc/tg-monitor
echo 'TOKEN' | sudo tee /etc/tg-monitor/token >/dev/null
sudo chmod 600 /etc/tg-monitor/token

Создай /usr/local/bin/tg-webhook-check.sh:


#!/bin/bash
# Проверка вебхука Telegram-бота. Выход 0 = норма, 1 = проблема.
set -u
TOKEN=$(cat /etc/tg-monitor/token)
EXPECTED_PREFIX="https://n8n.example.ru/webhook/"
MAX_PENDING=20
MAX_ERROR_AGE=600

INFO=$(curl -s --max-time 15 "https://api.telegram.org/bot${TOKEN}/getWebhookInfo")
if [ -z "$INFO" ] || [ "$(echo "$INFO" | jq -r '.ok')" != "true" ]; then
  echo "CRIT: api.telegram.org недоступен или токен невалиден"
  exit 1
fi

URL=$(echo "$INFO" | jq -r '.result.url')
PENDING=$(echo "$INFO" | jq -r '.result.pending_update_count')
ERR_DATE=$(echo "$INFO" | jq -r '.result.last_error_date // 0')
ERR_MSG=$(echo "$INFO" | jq -r '.result.last_error_message // ""')
NOW=$(date +%s)

case "$URL" in
  "$EXPECTED_PREFIX"*) ;;
  *) echo "CRIT: вебхук указывает не туда: '$URL'"; exit 1 ;;
esac

if [ "$PENDING" -gt "$MAX_PENDING" ]; then
  echo "WARN: в очереди $PENDING апдейтов"
  exit 1
fi

if [ "$ERR_DATE" -gt 0 ] && [ $((NOW - ERR_DATE)) -lt "$MAX_ERROR_AGE" ]; then
  echo "WARN: свежая ошибка вебхука: $ERR_MSG"
  exit 1
fi

echo "OK: $URL, pending=$PENDING"
exit 0

Где: сервер n8n.


sudo chmod 755 /usr/local/bin/tg-webhook-check.sh
sudo /usr/local/bin/tg-webhook-check.sh

Результат: строка OK с адресом вебхука. Дальше подключи скрипт к своему мониторингу. Для Zabbix это UserParameter, для простого варианта cron с отправкой письма.

Пример cron с письмом при проблеме. Нужен настроенный mailutils или любой sendmail-совместимый агент.

Где: сервер n8n, файл /etc/cron.d/tg-webhook-check.


*/5 * * * * root /usr/local/bin/tg-webhook-check.sh >/tmp/tg-check.out 2>&1 || mail -s "TG webhook problem" admin@example.ru < /tmp/tg-check.out

Не шли алерт о сломанном Telegram-боте через того же Telegram-бота. Звучит очевидно. Видел трижды.

В Zabbix добавь ещё веб-сценарий на https://n8n.example.ru/healthz с ожидаемым кодом 200 и триггер на срок действия сертификата. Сертификат обычно протухает в ночь на воскресенье, когда ты на даче.

Резервное копирование

Что Где лежит Как часто Зачем
База PostgreSQL том db_storage Ежедневно Workflow, credentials, история выполнений
.env /opt/n8n/.env При каждом изменении Ключ шифрования и пароли
Экспорт workflow в JSON через CLI n8n Ежедневно Быстро поднять отдельный workflow без отката базы
Конфиги docker-compose.yml, init-data.sh, nginx, cron При изменении Собрать сервер заново за 15 минут

Создай /usr/local/bin/n8n-backup.sh:


#!/bin/bash
set -euo pipefail
cd /opt/n8n
set -a; . ./.env; set +a

STAMP=$(date +%F_%H%M)
DEST=/opt/n8n/backup/$STAMP
mkdir -p "$DEST"

# Дамп базы в custom-формате
docker compose exec -T postgres pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc > "$DEST/n8n.dump"

# Экспорт workflow и зашифрованных credentials
docker compose exec -T n8n sh -c 'rm -rf /home/node/.n8n/export && mkdir -p /home/node/.n8n/export/wf /home/node/.n8n/export/cred'
docker compose exec -T n8n n8n export:workflow --backup --output=/home/node/.n8n/export/wf/
docker compose exec -T n8n n8n export:credentials --backup --output=/home/node/.n8n/export/cred/
docker compose cp n8n:/home/node/.n8n/export "$DEST/export"

# Конфиги
cp .env docker-compose.yml init-data.sh "$DEST/"
cp /etc/nginx/sites-available/n8n.conf "$DEST/"

tar -czf "/opt/n8n/backup/n8n_$STAMP.tar.gz" -C /opt/n8n/backup "$STAMP"
rm -rf "$DEST"
chmod 600 "/opt/n8n/backup/n8n_$STAMP.tar.gz"

# Храним 14 дней локально
find /opt/n8n/backup -name 'n8n_*.tar.gz' -mtime +14 -delete
echo "Backup done: /opt/n8n/backup/n8n_$STAMP.tar.gz"

Где: сервер n8n.


sudo chmod 700 /usr/local/bin/n8n-backup.sh
sudo /usr/local/bin/n8n-backup.sh
echo '30 3 * * * root /usr/local/bin/n8n-backup.sh >> /var/log/n8n-backup.log 2>&1' | sudo tee /etc/cron.d/n8n-backup

Результат: в /opt/n8n/backup появился архив с датой. Cron будет делать новый каждую ночь в 3:30.

Бэкап на том же сервере это не бэкап, это копия. Отправляй архивы наружу: rsync или rclone на другой сервер или в S3-совместимое хранилище. В архиве лежит ключ шифрования, поэтому хранилище тоже должно быть закрытым.

Восстановление

Проверь восстановление хотя бы раз до того, как оно понадобится. Бэкап, который ни разу не восстанавливали, это гипотеза.

Где: сервер n8n или новый сервер с тем же docker-compose.yml и .env из архива.


cd /opt/n8n
tar -xzf backup/n8n_ДАТА.tar.gz -C /tmp
cp /tmp/ДАТА/.env /tmp/ДАТА/docker-compose.yml /tmp/ДАТА/init-data.sh /opt/n8n/
docker compose up -d postgres
sleep 10
set -a; . ./.env; set +a
docker compose exec -T postgres pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --clean --if-exists --no-owner --role="$POSTGRES_NON_ROOT_USER" < /tmp/ДАТА/n8n.dump
docker compose up -d
docker compose logs n8n --tail 30

Результат: n8n стартует с теми же workflow и credentials. После старта опубликованные workflow перерегистрируют вебхуки. Проверь getWebhookInfo, что адрес правильный.

Обновление n8n

Рождённый в Docker обновления не боится. Если перед обновлением сделал бэкап.

Что проверить до обновления:

  • Прочитай заметки к релизам между текущей и целевой версией на GitHub, ищи breaking changes и упоминания Telegram.
  • Сделай свежий бэкап базы скриптом выше.
  • Запиши текущую версию: она понадобится для отката.
  • Обновляйся на stable, а не на beta. Beta это для стенда.

Где: сервер n8n, каталог /opt/n8n.


cd /opt/n8n
grep N8N_VERSION .env
sudo /usr/local/bin/n8n-backup.sh
sed -i 's/^N8N_VERSION=.*/N8N_VERSION=НОВАЯ_ВЕРСИЯ/' .env
docker compose pull n8n
docker compose up -d n8n
docker compose logs n8n -f --tail 50

Результат: в логах проходят миграции базы, если они есть, и n8n сообщает о готовности. Дальше healthz, getWebhookInfo и тестовое сообщение боту.

Откат после миграции базы
Новые версии n8n часто мигрируют схему базы. Старая версия с новой схемой может не запуститься или работать некорректно. Поэтому откат это всегда пара действий: вернуть прежнюю версию в .env и восстановить дамп базы, снятый перед обновлением. Только смена тега образа без отката базы откатом не является.

Переход с ветки 1.x на 2.x отдельная история. Ветка 2.0 вышла как релиз про усиление безопасности, с изменениями поведения по умолчанию. Такой переход делай сначала на копии, с отдельным тестовым ботом, и только потом в проде.

FAQ: частые вопросы про Telegram Trigger n8n

Почему Telegram Trigger в n8n не работает после настройки?

В большинстве случаев Telegram не может доставить апдейт на твой сервер. Выполни getWebhookInfo: пустой url значит, что workflow не опубликован, адрес с webhook-test значит, что тест затёр прод. Заполненное поле last_error_message прямо называет причину, сверяй с таблицей ошибок выше.

Как проверить, что Telegram Trigger работает правильно?

Три проверки. getWebhookInfo показывает боевой url с /webhook/ и ноль ошибок. В access.log nginx появляется POST с кодом 200 от подсетей Telegram, когда пишешь боту. В разделе Executions n8n появляется новое выполнение.

Что делать, если n8n пишет Bad Request: bad webhook: An HTTPS URL must be provided for webhook?

n8n передаёт Telegram адрес с http или localhost. Задай переменную WEBHOOK_URL с публичным HTTPS-адресом, настрой TLS на reverse proxy и пересоздай контейнер командой docker compose up -d. После этого сними workflow с публикации и опубликуй снова.

Чем Telegram Trigger отличается от обычной Webhook-ноды?

Telegram Trigger сам вызывает setWebhook при публикации, сам генерирует и проверяет секретный токен и разбирает тип апдейта. Webhook-нода принимает любой POST, вебхук у Telegram регистрируешь вручную, а проверку секрета пишешь сам. Взамен получаешь предсказуемый путь и полный контроль над параметрами.

Можно ли использовать несколько Telegram Trigger с одним ботом?

Нет. У бота один вебхук, и последний опубликованный workflow забирает все апдейты себе. Делай один workflow-роутер с триггером и раскидывай логику по под-workflow через Execute Workflow. Для тестов заведи отдельного бота.

Почему тестовый запуск висит в Waiting for trigger event?

Reverse proxy не пропускает websocket, и редактор не узнаёт о пришедшем апдейте. Добавь в nginx proxy_http_version 1.1 и заголовки Upgrade и Connection. Вторая частая причина: ты пишешь боевому боту, а слушает тестовый, или наоборот.

Как узнать chat id в n8n?

Напиши боту любое сообщение и открой выполнение Telegram Trigger. Chat ID лежит в поле message.chat.id. У личных чатов он положительный, у групп отрицательный, у супергрупп и каналов начинается с -100.

Почему бот в группе не видит сообщения?

Включён режим приватности: бот видит только команды и ответы на свои сообщения. Отключи его в BotFather командой /setprivacy и переподключи бота к группе. Либо дай боту права администратора.

Нужен ли белый IP для Telegram-бота на n8n?

Для Telegram Trigger нужен адрес, доступный из интернета по HTTPS. Белый IP не обязателен: подойдёт туннель или проксирующий VPS. Если входящий доступ невозможен в принципе, используй polling через Schedule и getUpdates.

Прогноз

Мы разобрали, как на самом деле ходит апдейт: от пользователя через серверы Telegram на твой nginx и дальше в n8n. Подняли n8n в Docker с Postgres за HTTPS, правильно задали WEBHOOK_URL, включили websocket в прокси. Собрали бота по схеме «один триггер, один роутер» с командами, кнопками и ответами на callback. Закрыли сервер фаерволом, fail2ban и ограничением вебхука по подсетям Telegram.

Теперь у тебя бот, который переживает перезагрузку, обновление и ночное падение. Мониторинг заметит протухший вебхук раньше пользователей, бэкап восстановится вместе с ключом шифрования, а при поломке есть алгоритм на пять минут вместо гадания. Главная привычка, которую стоит унести: бот молчит, первым делом getWebhookInfo. Не перезагрузка, не переустановка, а один запрос к API.

Не заработало?
Если бот всё ещё молчит, пиши в комментарии, разберёмся. Приложи вывод getWebhookInfo без токена, версию n8n и кусок конфига nginx. По этим трём вещам причина находится в девяти случаях из десяти.
Андрей А.
Author: Андрей А.

25 years in IT — from a field engineer at MegaFon to head of department. I rebuild IT infrastructure that’s falling apart and turn it into something predictable. I know what chaos costs. I help businesses get out of a crisis: cleaning up legacy systems, stabilizing what’s breaking, and setting up processes you can actually plan around.

Оставайтесь на связи

Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.

Подписаться на IT-Аптеку →
Поделитесь:

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх