Telegram Trigger n8n не ловит сообщения в 9 случаях из 10 по одной причине: Telegram не может достучаться до твоего n8n по HTTPS. Ниже рабочая схема, конфиги и команды диагностики. Без воды.
- Публичный домен с валидным 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.
- Нет HTTPS или n8n не знает свой публичный адрес. Без WEBHOOK_URL n8n регистрирует вебхук на http://localhost:5678. Telegram такой адрес не примет и вернёт ошибку bad webhook.
- Невалидный сертификат. Самоподписанный без загрузки в setWebhook, просроченный или без промежуточной цепочки. Telegram не сможет установить TLS и будет копить апдейты.
- Неподдерживаемый порт. Вебхуки Telegram работают только на 443, 80, 88 и 8443. Порт 5678 или 9443 не пройдёт.
- Reverse proxy без websocket. Редактор n8n получает события через websocket. Без заголовков Upgrade тестовый запуск висит в Waiting for trigger event, даже если апдейт дошёл.
- Конфликт вебхуков. Второй workflow с тем же ботом, тестовый запуск поверх боевого, сторонний скрипт с тем же токеном.
- Режим приватности в группах. По умолчанию бот в группе видит только команды и ответы на свои сообщения. Обычный текст до него не доходит.
- Сеть между Telegram и сервером. Фаервол режет подсети Telegram, хостер блокирует входящие или сервер в сети, где Telegram ограничен.
Рецепт: настройка 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. В ответ придёт токен: число, двоеточие и длинная строка из букв и цифр.
Сразу реши вопрос с группами. Если бот должен читать обычные сообщения в группе, отключи режим приватности: в 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.
Создай файл /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, создай учётку владельца. Дальше:
- Меню слева, раздел Credentials, кнопка Create.
- Найди Telegram API.
- Вставь токен в поле Access Token.
- Сохрани. 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. Тест на тестовом боте и публикация
Порядок, который не ломает прод:
- Скачай workflow через меню Download, импортируй копию, назови её с суффиксом TEST.
- В копии поменяй credential триггера и всех Telegram-нод на TG test bot.
- Нажми Execute workflow и напиши тестовому боту. Выполнение должно появиться сразу.
- Отладил, перенеси изменения в боевой 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, потом опубликуй снова.
Где: сервер 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
Где: сервер 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 и тестовое сообщение боту.
Переход с ветки 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.
Оставайтесь на связи
Рецепты от IT-боли. Без воды, без рекламы, без маркетинговой шелухи.
Подписаться на IT-Аптеку →




