9 мин чтенияИнтеграции

Self-hosted автоматизация n8n для ссылок: от Docker до webhook

Запустите self-hosted автоматизацию ссылок n8n с webhook Elido, Docker Compose и режимом очереди, удерживая данные workflow на инфраструктуре, которую вы контролируете, в ЕС.

Marius Voß
DevRel · edge infra
Self-hosted автоматизация ссылок n8n: webhook Elido проходят через реверс-прокси в инстанс n8n с очередью и воркерами, все внутри инфраструктуры, которую вы контролируете

Self-hosted автоматизация ссылок n8n означает три вещи, работающие на серверах, которые вы контролируете: инстанс n8n в Docker, публичный HTTPS endpoint, принимающий события webhook Elido, и исходящие вызовы обратно к API Elido с токеном ограниченной области действия. События ссылок и доменов приходят подписанными, а события по каждому клику - в дорожной карте. Ваши workflow решают, что делать дальше, а журналы выполнения никогда не покидают вашу инфраструктуру.

Это вся архитектура целиком. Остальная часть этого поста - то, что быстрые старты обычно пропускают: как провести webhook через реверс-прокси, как проверить подпись, чтобы посторонний не мог запустить ваши workflow, что меняет режим очереди и когда n8n Cloud - честно говоря, лучший выбор. Я запускаю эту настройку на одной небольшой виртуальной машине, и все движущиеся части умещаются на одном экране.

Если вы хотите разместить сами ссылки тоже на собственном железе, это отдельная и намного более крупная задача. Плейбук по self-hosting Elido на k3s разбирает ее. Здесь Elido остается управляемым сервисом, и только слой автоматизации переезжает к вам.

Зачем размещать n8n у себя для автоматизации ссылок

Обычная причина - данные. Workflow self-hosted URL-шортенера на n8n видит каждый payload, который он обрабатывает: целевой URL, теги, иногда название кампании, которое говорит больше, чем хотелось бы, а также страну, устройство и referrer, если вы подтягиваете туда аналитику кликов. В n8n Cloud эти выполнения хранятся на чужих серверах по чужим настройкам хранения по умолчанию. На self-hosted они лежат в вашем Postgres, в выбранном вами регионе.

Согласно статье 28 GDPR каждому обработчику, который касается персональных данных, нужен договор и место в ваших записях. Меньше обработчиков - меньше бумажной работы. Если ваши маркетинговые данные и так должны оставаться в ЕС, руководство по резидентности данных в ЕС объясняет, почему слой автоматизации учитывается ровно так же, как и сам шортенер.

Вторая причина - форма затрат. n8n Cloud берет плату по количеству выполнений, а активный триггер webhook или частый опрос аналитики быстро сжигает выполнения. На self-hosted выполнение - это строка в таблице и несколько миллисекунд CPU.

Третья причина - охват. Self-hosted инстанс находится в той же сети, что и база данных вашей CRM или внутренний тикет-трекер, так что событие ссылки может попасть в систему, которая никогда не была предназначена для выхода в интернет.

Стек Docker Compose

Минимальному стеку автоматизации ссылок n8n в Docker нужно четыре сервиса. Собственное руководство по Docker Compose от n8n - справочник; это версия, с которой я бы начала для работы со ссылками, с уже включенным режимом очереди, чтобы не пришлось мигрировать позже.

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${PG_PASSWORD}
      POSTGRES_DB: n8n
    volumes: [pgdata:/var/lib/postgresql/data]

  redis:
    image: redis:7

  n8n:
    image: docker.n8n.io/n8nio/n8n
    env_file: .env.n8n
    ports: ["127.0.0.1:5678:5678"]
    depends_on: [postgres, redis]

  n8n-worker:
    image: docker.n8n.io/n8nio/n8n
    command: worker
    env_file: .env.n8n
    depends_on: [n8n]

volumes:
  pgdata:

И общий файл окружения:

DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=change-me
EXECUTIONS_MODE=queue
QUEUE_BULL_REDIS_HOST=redis
N8N_ENCRYPTION_KEY=generate-a-long-random-string
N8N_WEBHOOK_URL=https://n8n.example.com/
N8N_PROXY_HOPS=1

Две строки важнее, чем кажутся на вид. Ключ шифрования должен быть идентичен на основном процессе и на каждом воркере, иначе воркеры не смогут расшифровать учетные данные Elido, и каждое выполнение будет падать с запутанной ошибкой авторизации. А порт 5678 привязан только к localhost, потому что единственное, что должно смотреть в интернет, - это реверс-прокси.

Self-hosted архитектура n8n для автоматизации ссылок: Elido отправляет подписанные webhook по HTTPS на реверс-прокси, который перенаправляет их основному процессу n8n, который ставит выполнения в очередь Redis для воркеров с Postgres в качестве хранилища, а воркеры вызывают API Elido исходящими запросами с токеном ограниченной области действия

Вызов API Elido из self-hosted n8n

Для исходящих вызовов не нужно ничего сверх того, что уже поставляется с n8n. Создайте учетные данные Header Auth с именем Authorization и значением Bearer , за которым следует API-ключ из дашборда Elido, а затем используйте их из встроенного узла HTTP Request. n8n шифрует их на хранении ключом из файла окружения, что еще одна причина, по которой этот ключ должен совпадать повсюду.

Каждый маршрут ссылок привязан к рабочему пространству. Чтобы сократить URL, отправьте POST на https://api.elido.app/v1/workspaces/{workspace_id}/links с JSON-телом:

{
  "domain_id": 12,
  "destination_url": "{{ $json.url }}",
  "title": "Spring launch",
  "tags": ["n8n", "spring"]
}

Оставьте slug пустым, и Elido сгенерирует его сам. domain_id - это короткий домен, на котором живет ссылка; GET-запрос на /v1/workspaces/{workspace_id}/domains перечисляет ваши, и я бы зафиксировала ID жестко в workflow, а не искала его при каждом запуске. GET на том же маршруте ссылок перечисляет ссылки, а PATCH /v1/workspaces/{workspace_id}/links/{link_id} меняет назначение или теги постфактум.

Задайте заголовок Idempotency-Key на POST-запросе, собранный из чего-то стабильного в запускающем элементе, например ID строки. Если n8n повторяет узел после таймаута, API воспроизводит первый ответ вместо создания второй ссылки. Быстрый старт по API и SDK описывает остальную поверхность, а страница API и SDK пригодится, если позже вы захотите перенести шаг в код.

Есть также готовый community-узел, n8n-nodes-elido, призванный обернуть эти вызовы в более дружелюбную форму. Он опубликован в npm (версия 0.2.0), но все еще остается опциональным, а поскольку это не верифицированный узел, он устанавливается только в self-hosted n8n, который и предполагает этот пост. Держите версию с HTTP Request как базовый вариант. Если вы все же устанавливаете community-пакеты в режиме очереди, помните, что GUI устанавливает их только в основной контейнер; воркеры их никогда не увидят. Начиная с n8n 2.21, установка через переменные окружения решает это, синхронизируя каждый контейнер при старте, хотя при первом запуске она удаляет все, чего нет в ее списке.

Проведение webhook Elido через ваш реверс-прокси

События идут в обратную сторону. Elido отправляет link.created, link.updated и domain.verified (среди прочих) на endpoint, который вы регистрируете в Settings, Webhooks, а в self-hosted n8n этот endpoint - production-URL узла Webhook. Событие click.created для каждого клика в дорожной карте, но пока не выпущено, так что сейчас данные о кликах приходят из запланированного опроса API аналитики. Полный список событий и форма payload - в справочнике webhook для событий ссылок.

За прокси n8n собирает свой URL webhook из собственных протокола, хоста и порта, а это значит, что он с готовностью объявит http://localhost:5678/webhook/... любому, кто спросит. Страница настройки реверс-прокси короткая, и ее стоит прочитать целиком: задайте N8N_WEBHOOK_URL как ваш публичный адрес, задайте N8N_PROXY_HOPS=1 и настройте, чтобы последний прокси передавал X-Forwarded-For, X-Forwarded-Host и X-Forwarded-Proto. В более старых руководствах говорится про WEBHOOK_URL; текущие релизы все еще читают ее, но логируют предупреждение об устаревании.

Nginx, Traefik, что угодно из того, что вы уже используете, подойдет. Важно, чтобы на публичной стороне был TLS и чтобы путь /webhook/* доходил до n8n без изменений.

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

Настраиваете это для клиента и хотите, чтобы сторона webhook была разобрана за вас? Страница функции webhook показывает, на что можно подписаться, прежде чем вы начнете что-либо строить.

Проверка подписи, прежде чем что-либо запускать

Публичный URL webhook - это публичный URL. Любой, кто его найдет, может отправить поддельное событие POST-запросом и запустить ваш workflow, так что первым узлом после триггера должна быть проверка подписи.

Каждая доставка Elido несет X-Webhook-Signature (значение v1= плюс hex-дайджест), X-Webhook-Timestamp в секундах Unix, X-Webhook-Event и X-Webhook-Delivery. Дайджест - это HMAC-SHA256 по метке времени, точке и сырому телу запроса, с ключом - секретом whsec_, показанным один раз при создании endpoint.

Сначала включите Raw Body в узле Webhook. Это тот шаг, который люди пропускают. Если вы хешируете JSON.stringify($json.body) вместо точных байтов, отправленных Elido, порядок ключей или пробелы отличаются, и каждая подпись не совпадет, а вы потратите вечер, будучи уверены, что секрет неверный. Затем узел Code:

const crypto = require("crypto");
const item = $input.first();
const h = item.json.headers;
const raw = (await this.helpers.getBinaryDataBuffer(0, "data")).toString(
  "utf8",
);

const ts = Number(h["x-webhook-timestamp"]);
if (Math.abs(Date.now() / 1000 - ts) > 300) throw new Error("stale delivery");

const expected =
  "v1=" +
  crypto
    .createHmac("sha256", $env.ELIDO_WEBHOOK_SECRET)
    .update(`${ts}.${raw}`)
    .digest("hex");
const got = h["x-webhook-signature"] || "";
if (
  got.length !== expected.length ||
  !crypto.timingSafeEqual(Buffer.from(got), Buffer.from(expected))
) {
  throw new Error("bad signature");
}
return [{ json: JSON.parse(raw) }];

Модуль crypto доступен в узле Code по умолчанию, так что дополнительная настройка тут не нужна. Пятиминутное окно блокирует повторное воспроизведение перехваченного запроса. Когда вы ротируете секрет, Elido также отправляет X-Elido-Signature-Previous в течение льготного периода, так что вы можете принимать любой из двух ключей, пока обновляете переменную. В руководстве по проверке подписей webhook есть версия этого узла, которая проверяет оба заголовка, а также такая же проверка в Node, Python и Go.

Проверка подписи webhook в self-hosted n8n: считать метку времени и сырое тело, отклонить доставки старше 300 секунд, заново вычислить HMAC-SHA256 по метке времени, точке и сырому телу с секретом endpoint, сравнить за постоянное время, затем либо запустить workflow, либо остановить выполнение

Режим очереди и повторы

Когда проверка подписи на месте, остается вопрос, что происходит под нагрузкой или когда что-то недоступно. Здесь задействованы две системы повторов, и они покрывают разные виды сбоев.

В режиме очереди n8n основной процесс принимает webhook, создает выполнение и передает его ID в Redis; воркеры его забирают. Всплеск событий накапливается в очереди вместо того, чтобы задерживать ответ HTTP. Для большего входящего объема можно добавить выделенные обработчики webhook за балансировщиком нагрузки, хотя для большинства нагрузок, связанных со ссылками, вполне хватает одного основного процесса и двух воркеров.

Сторона Elido справляется с недоступностью n8n. Любой ответ не 2xx или таймаут повторяется через 1, 5 и 15 минут; после трех попыток доставка помечается неудачной, и вы можете перезапустить ее из дашборда. Это покрывает перезапуск контейнера или быстрый деплой. Это не покрывает простой на выходных, поэтому я бы сочетала webhook с ночной сверкой, которая перечисляет ссылки через API, как утверждает статья webhook против опроса.

Используйте X-Webhook-Delivery как ключ идемпотентности. Повторы используют то же значение.

Self-hosted n8n против n8n Cloud: честные компромиссы

Я предпочитаю self-hosting для этой задачи, но видела, как команды об этом жалели. Вот сравнение без рекламных лозунгов с любой из сторон.

ВопросSelf-hosted n8nn8n Cloud
Где живут данные выполненияНа ваших серверах, в вашем регионе, с вашим хранениемНа инфраструктуре n8n по умолчанию n8n
Доступ к внутренним системамТа же сеть, что и ваша CRM и базы данныхТолько то, что вы открыли публично
Публичный URL webhook и TLSПрокси и сертификаты настраиваете выПредоставляется
Обновления, бэкапы, патчиВаша работа, каждый месяцОбрабатывается за вас
Стоимость при высоком объеме событийФиксированная стоимость сервераРастет вместе с количеством выполнений

Последняя строка режет в обе стороны. Небольшая виртуальная машина дешева, но час инженера на сломанном обновлении - нет, а n8n выпускает обновления часто. Если в команде никто не владеет этой машиной, Cloud с узлом HTTP Request и REST API Elido - более разумный вариант, а рецепты для Make и IFTTT или руководство по Zapier показывают, как выглядит этот управляемый путь на других платформах.

Чего я не могу вам сказать, так это примет ли ваш специалист по защите данных self-hosted машину как более простую, чем DPA поставщика. По моему опыту, обычно принимает, но это зависит от того, насколько хорошо эта машина управляется. О том, как работает сторона договора шортенера, начните с руководства по GDPR для URL-шортенеров. А если вы готовы собрать первый workflow, получите токен API для рабочего пространства и направьте на него узел Webhook.

По теме в блоге

Частые вопросы

Можно ли использовать URL-шортенер с self-hosted n8n?

Да. Self-hosted n8n может вызывать любой шортенер с REST API через встроенный узел HTTP Request. Для Elido это означает учетные данные Header Auth с вашим API-ключом и POST на маршрут ссылок в рамках рабочего пространства. Входящие события вроде link.created приходят через встроенный узел Webhook n8n, которому нужен публичный HTTPS URL. Событие click.created для каждого клика запланировано, но пока недоступно.

Как установить community-узлы в self-hosted n8n?

На одном инстансе используйте Settings, затем Community Nodes, и вставьте имя пакета npm. В режиме очереди установка через GUI не доходит до ваших воркеров, так что устанавливайте пакет внутри каждого контейнера, либо, начиная с n8n 2.21, перечислите его в N8N_COMMUNITY_PACKAGES с N8N_COMMUNITY_PACKAGES_MANAGED_BY_ENV, установленным в true. Для Elido он вам не нужен: узел HTTP Request покрывает API.

Что такое WEBHOOK_URL в n8n?

Она сообщает n8n, какой публичный адрес показывать в редакторе и регистрировать у внешних сервисов, потому что за реверс-прокси n8n не может определить это по собственным хосту и порту. Текущие версии n8n читают N8N_WEBHOOK_URL и логируют предупреждение об устаревании для более старой WEBHOOK_URL. Сочетайте это с N8N_PROXY_HOPS=1 и передаваемыми заголовками на прокси.

Как проверить подпись webhook в n8n?

Включите опцию Raw Body в узле Webhook, затем добавьте узел Code, который заново вычисляет HMAC-SHA256 по заголовку timestamp, точке и сырому телу, используя секрет вашего endpoint. Сравните ее с заголовком подписи за постоянное время и отклоняйте все, что старше пяти минут. Проверяйте по сырым байтам, никогда по пересериализованному JSON.

Нужен ли режиму очереди n8n Redis?

Да. В режиме очереди основной инстанс и любые обработчики webhook превращают входящие триггеры в ID выполнения и помещают их в очередь на основе Redis, а воркеры забирают их оттуда. n8n также не рекомендует режим очереди с SQLite, так что планируйте Postgres. Каждый процесс должен использовать один и тот же ключ шифрования, иначе воркеры не смогут прочитать сохраненные учетные данные.

Лучше ли self-hosted n8n для GDPR, чем n8n Cloud?

Может быть лучше, потому что вы выбираете, где физически находятся данные workflow, журналы выполнения и учетные данные, и кто ими администрирует. Это не делает вас автоматически соответствующими требованиям: вы становитесь ответственны за патчи, контроль доступа, резервное копирование и хранение. Для автоматизации ссылок, обрабатывающей данные о кликах, self-hosting в регионе ЕС убирает одного обработчика из ваших записей, что упрощает бумажную работу.

Попробуйте Elido

Вставьте URL - получите короткую ссылку

Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.

Бесплатно, без регистрации · 2 в день

Попробуйте Elido

URL-сокращатель с хостингом в ЕС: собственные домены, глубокая аналитика, открытый API. Бесплатный тариф - без банковской карты.

Теги
self-hosted n8n automation links
n8n self hosted url shortener
n8n docker link automation
n8n webhook signature verification
n8n queue mode
n8n http request node api

Читать дальше