Telegram-бот для Obsidian на n8n. Часть II: разбор стека и запуск workflow n8n в продакшн

24.03.2026

В предыдущей статье мы рассмотрели логику работы проекта, познакомились с системными и аппаратными требованиями, узнали о настройках DNS, создали Telegram-бота и научились получать и правильно сохранять API-токен и Chat ID.

Теперь настало время углубиться в технические детали реализации проекта, протестировать его и выпустить в продакшн. В этой статье вас ждет следующее:

  1. Разбор стека, свойств и настроек сервисов.
  2. Запуск стека на VPS.
  3. Детальный анализ workflow n8n.
  4. Запуск и тестирование workflow n8n в продакшен.
  5. Выводы.

Цикл статей построен так, что попробовать запустить проект на собственных мощностях и модернизировать его под индивидуальные задачи можно сразу. Достаточно, чтобы был подходящий по конфигурации VPS с установленным на нем Docker. Исходники проекта лежат в двух репозиториях: compose-файл для установки и запуска стека находится здесь, а отсюда можно скачать workflow для его запуска в n8n.

Стек

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

В нашем стеке n8n — это корневой сервис, который выполняет основную работу: принимает сообщения от пользователя через Telegram, передает инструкции в LLM и сохраняет заметку в Obsidian с помощью API. Другие сервисы — вспомогательные. Отдельно стоит отметить Caddy — обратный прокси-сервер, который обеспечивает TLS-соединение между микросервисами и предоставляет HTTPS-доступ к домену и поддоменам.

Состав стека и свойства сервисов

Caddy — обратный прокси-сервер с автоматически выпускаемыми TLS-сертификатами. Фишка Caddy в том, что он сам выпускает сертификаты Let’s Encrypt и сам следит за их автообновлением. Мы используем это свойство для автоматического выпуска TLS-сертификатов для n8n и Obsidian.

Сервис Caddy в составе compose-файла имеет следующие свойства:

 caddy: # Реверс‑прокси (TLS/HTTPS, маршрутизация на сервисы по домену)
   image: caddy:latest # Образ Caddy (внутренний CA, автосертификаты, проксирование)
   container_name: caddy # Фиксированное имя контейнера (удобно для docker exec/logs)
   restart: unless-stopped # Автоперезапуск контейнера, пока он не будет остановлен в ручном режиме
   ports:
      - "${CADDY_HTTP_HOST_PORT}:${CADDY_HTTP_CONTAINER_PORT}" # HTTP с хоста -> Caddy (обычно 80:80)
      - "${CADDY_HTTPS_HOST_PORT}:${CADDY_HTTPS_CONTAINER_PORT}" # HTTPS с хоста -> Caddy (обычно 443:443)
env_file:
      - .env # Подгружаем переменные из файла .env для шаблонов и настроек
   volumes:
      - caddy_data:/data # Данные Caddy (в т.ч. PKI/сертификаты) — общий том для n8n
      - caddy_config:/config # Служебная конфигурация Caddy
      - html_notes:/html_notes:ro # Статика (HTML) из mkdocs — только чтение
      - ./Caddyfile.template:/etc/caddy/Caddyfile.template:ro # Шаблон Caddyfile (с ${...}) — только чтение
# Команда запуска Caddy делает 3 вещи:
# 1) Ставит `gettext` (в нем есть `envsubst`) -- если пакет уже есть, продолжаем без ошибки.
# 2) Генерирует итоговый `/etc/caddy/Caddyfile` из шаблона `/etc/caddy/Caddyfile.template`, подставляя переменные окружения из `.env` (через `env_file`).
# 3) Стартует Caddy с получившимся конфигом.
# Важно: комментарии нельзя вставлять внутрь строки /bin/sh -c "..." - это ломает команду.

   command: >
      /bin/sh -c "
      apk add --no-cache gettext >/dev/null 2>&1 || true;
      envsubst < /etc/caddy/Caddyfile.template > /etc/caddy/Caddyfile &&
      caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
   "
   networks:
      - ollama-network # Общая сеть для проксируемых сервисов
   depends_on:
      - mkdocs # Ждем контейнер mkdocs, чтобы статика была доступна
 

caddy-perms-fixer — сервис, который автоматически (каждые 300 секунд) выставляет нужные права на чтение и владельца для TLS-сертификатов Caddy, чтобы n8n мог их прочитать. Сервис в compose-файле выглядит следующим образом:

 caddy-perms-fixer: # Фикс прав на PKI Caddy, чтобы n8n мог читать root.crt
   image: alpine:latest # Легкий образ для запуска shell-скрипта
   container_name: caddy-perms-fixer # Фиксированное имя контейнера
   restart: unless-stopped # Автоперезапуск контейнера, пока он не будет остановлен в ручном режиме
   volumes:
      - caddy_data:/data # Монтируем тот же том, где Caddy хранит /data/caddy/pki/...
      - ./fix-caddy-pki-perms.sh:/usr/local/bin/fix-caddy-pki-perms.sh:ro # Скрипт коррекции прав — только чтение
   command: ["/bin/sh", "-c", "while true; do /usr/local/bin/fix-caddy-pki-perms.sh; sleep 300; done"] # Переодически приводит права к читаемым
   networks:
      - ollama-network # Сеть не обязательна, но унифицируем
 

ollama — фреймворк для запуска LLM локально как на GPU, так и на CPU. Модели скачиваются отдельно через Open WebUI и хранятся в томе ollama_data:/root/.ollama.

Сервис в стеке представлен так:

 ollama: # Локальный LLM-сервер (API для моделей)
   image: ollama/ollama:latest # Используется последняя версия Ollama
   container_name: ollama # Имя контейнера (на него ссылаются другие сервисы по DNS в сети)
   restart: unless-stopped # Автоперезапуск
   volumes:
      - ollama_data:/root/.ollama # Хранилище моделей/кэша Ollama (переживает перезапуски)
   networks:
      - ollama-network # Сеть, чтобы Open WebUI и n8n ходили на http://ollama:...
   environment:
      - OLLAMA_NUM_PARALLEL=${OLLAMA_NUM_PARALLEL} # Параллельные запросы/генерации (из .env)
      - OLLAMA_MAX_LOADED_MODELS=${OLLAMA_MAX_LOADED_MODELS} # Сколько моделей держать загруженными (из .env)
      - OLLAMA_MODELS=/root/.ollama/models # Путь каталога моделей внутри контейнера
   dns:
      - 1.1.1.1 # Явно задаём DNS (Cloudflare) так как в образе по умолчанию DNS не задан
      - 1.0.0.1 # Второй DNS
 

open-webui — открытый web UI для общения с Ollama. Сервис не имеет специальных настроек и представлен в стеке следующим образом:

 open-webui: # Веб‑интерфейс для общения с Ollama
   image: ghcr.io/open-webui/open-webui:main # Образ Open WebUI
   container_name: open-webui # Имя контейнера
   restart: unless-stopped # Автоперезапуск
   volumes:
      - open-webui_data:/app/backend/data # Данные WebUI (пользователи/настройки/история)
   depends_on:
      - ollama # Ожидание запуска контейнера с Ollama
   environment:
      - OLLAMA_BASE_URL=http://ollama:${OLLAMA_CONTAINER_PORT} # URL Ollama внутри docker-сети (порт из .env)
   networks:
      - ollama-network # Общая сеть
   extra_hosts:
      - host.docker.internal:host-gateway # Доступ из контейнера к хосту по имени host.docker.internal
 

n8n — low-code, node-based фреймворк для автоматизации процессов. n8n имеет множество настроек через переменные окружения, самые важные из них: WEBHOOK_URL — базовый URL для вебхуков, необходимый для интеграции с Telegram; NODE_EXTRA_CA_CERTS — путь к TLS-сертификатам, которым должен доверять n8n. Чтобы n8n видел TLS-сертификаты по пути, установленному переменной окружения NODE_EXTRA_CA_CERTS, нужно примонтировать том caddy_data:/caddy_data:ro в режиме только для чтения.

Сервис в compose-файле выглядит следующим образом:

 n8n: # Автоматизация/воркфлоу (UI + вебхуки)
   image: n8nio/n8n:${N8N_VERSION} # Версия n8n фиксируется через .env
   container_name: n8n # Имя контейнера
   restart: unless-stopped # Автоперезапуск
   volumes:
      - n8n_data:/home/node/.n8n # Персистентные данные n8n (учётные записи и workflows)
      - caddy_data:/caddy_data:ro # Read‑only доступ к CA Caddy (root.crt) через общий том
   depends_on:
      - ollama # Ожидание старта контейнера с Ollama
   environment:
      - N8N_HOST=n8n.${DOMAIN} # Публичный хост UI n8n (используется для ссылок/redirect)
      - N8N_PROTOCOL=https # Протокол снаружи (через Caddy TLS)
      - N8N_PORT=${N8N_CONTAINER_PORT} # Порт, который слушает n8n внутри контейнера (подтягивается из .env)
      - WEBHOOK_URL=https://${DOMAIN}/ # Базовый URL для вебхуков (важно для внешних интеграций)
      - N8N_EDITOR_BASE_URL=https://${DOMAIN}/ # База URL редактора (чтобы UI генерил правильные ссылки)
      - N8N_SECURE_COOKIE=${N8N_SECURE_COOKIE} # Secure-cookie для HTTPS (true/false из .env)
      - NODE_EXTRA_CA_CERTS=/caddy_data/caddy/pki/authorities/local/root.crt # Доверять самоподписанному CA Caddy (TLS)
   networks:
      - ollama-network # Общая сеть
 

netdata — сервис мониторинга потребления ресурсов VPS, аналог Grafana, но с более дружелюбным интерфейсом и готов к использованию из коробки. Настроек у сервиса очень много, но для нашего проекта критически важных среди них нет.

В стеке сервис выглядит следующим образом:

 netdata: # Мониторинг хоста и контейнеров (метрики/дашборды)
   image: netdata/netdata:latest # Последняя версия образа Netdata
   container_name: netdata # Имя контейнера
   hostname: ${NETDATA_HOSTNAME} # Отображаемое имя хоста в UI Netdata (из .env)
   restart: unless-stopped # Автоперезапуск
   cap_add:
      - SYS_PTRACE # Нужно для части системных метрик/профилирования
      - SYS_ADMIN # Нужно для части системных метрик (высокие привилегии)
   security_opt:
      - apparmor:unconfined # Убираем ограничения AppArmor, иначе часть метрик может не работать
   volumes:
      - netdataconfig:/etc/netdata # Конфиги Netdata
      - netdatalib:/var/lib/netdata # База/состояние Netdata
      - netdatacache:/var/cache/netdata # Кэш Netdata
      - /etc/passwd:/host/etc/passwd:ro # Для маппинга UID->имена пользователей # Для маппинга UID->имена пользователей
      - /etc/group:/host/etc/group:ro # Для маппинга GID->группы # Для маппинга GID->группы
      - /etc/localtime:/etc/localtime:ro # Корректное время/таймзона в контейнере
      - /proc:/host/proc:ro # Метрики процессов/ядра
      - /sys:/host/sys:ro # Метрики системы/устройств
      - /etc/os-release:/host/etc/os-release:ro # Информация об ОС
      - /var/log:/host/var/log:ro # (Опционально) чтение логов хоста для интеграций
      - /var/run/docker.sock:/var/run/docker.sock:ro # Доступ к Docker API для метрик контейнеров
   networks:
      - ollama-network # Общая сеть (если проксируете Netdata через Caddy)
   environment:
      - NETDATA_DISABLE_CLOUD=1 # Отключаем Netdata Cloud (не шлём метрики наружу)
 

obsidian — Markdown-система заметок с раздельными хранилищами и graph-представлением связей между заметками. Obsidian — простой сервис, специальных или сложных настроек у него нет. Сервис в compose-файле выглядит следующим образом:

 obsidian: # Obsidian в контейнере (веб/VNC‑интерфейс, хранение vault)
   image: lscr.io/linuxserver/obsidian:latest # LinuxServer.io образ Obsidian
   container_name: obsidian # Имя контейнера
   restart: unless-stopped # Автоперезапуск
   security_opt:
      - seccomp:unconfined # Снимаем seccomp-ограничения (иногда нужно для GUI/Chromium внутри)
   environment:
      - PUID=${PUID:-1000} # UID владельца файлов в примонтированных томах (по умолчанию 1000)
      - PGID=${PGID:-1000} # GID владельца файлов (по умолчанию 1000)
      - TZ=${TZ:-Europe/Moscow} # Таймзона контейнера
      - CUSTOM_USER=${OBSIDIAN_USER:-admin} # Логин для доступа к UI (из .env)
      - PASSWORD=${OBSIDIAN_PASSWORD:-admin} # Пароль для доступа к UI (из .env)
      - NO_DECOR=true # Убрать декорации окна (косметика UI)
      - NO_FULL=true # Не разворачивать на весь экран по умолчанию
      - DISABLE_IPV6=true # Отключить IPv6 внутри контейнера
      - LC_ALL=en_US.UTF-8 # Локаль (чтобы корректно работали шрифты/кодировки)
   volumes:
      - obsidian_config:/config # Конфиги и состояние приложения
      - obsidian_vaults:/config/vaults # Vault'ы Obsidian (используются также mkdocs)
   ports:
      - "${OBSIDIAN_HOST_PORT}:${OBSIDIAN_CONTAINER_PORT}" # Порт UI Obsidian на хосте -> в контейнер
      - "${OBSIDIAN_API_PORT_HOST}:${OBSIDIAN_API_PORT_CONTAINER}" # (Если нужно) API/служебный порт
   devices:
      - /dev/dri:/dev/dri # Проброс GPU (ускорение рендера/кодеков)
   shm_size: "1gb" # Shared memory для браузера/GUI (уменьшает вылеты от нехватки /dev/shm)
   networks:
      - ollama-network # Общая сеть
 

article-parser — самописный Python-сервис парсинга текста статей по URL с очисткой от HTML-кода. Назначение сервиса — подготовка текстов статей для прочтения LLM. Если в LLM отправить просто ссылку на статью, то LLM откроет статью как web-страницу с большим количеством ненужного HTML-кода. Сервис удаляет все элементы страницы, кроме самого текста статьи.

Такой подход экономит ресурсы LLM и время на обработку текста. У сервиса нет специальных настроек, а в составе compose-файла он представлен следующим образом:

 article-parser: # Внутренний сервис парсинга статей (доступен в docker-сети)
   image: article_parser:latest # Локально собранный образ (смотрите /root/articles_parser)
   container_name: article-parser # Имя контейнера
   restart: unless-stopped # Автоперезапуск
   expose:
      - "${ARTICLE_PARSER_PORTS}" # Открываем порт только внутри сети (не публикуем на хост)
   networks:
      - ollama-network # Общая сеть
 

MkDocs — система сборки документации из Markdown-файлов на Python. В нашем проекте MkDocs автоматически конвертирует заметки, сделанные в Obsidian, в HTML-страницы с удобным доступом по URL. Сам же Obsidian в бесплатной версии не предоставляет подобной возможности. Поэтому у нас подключен том Obsidian к MkDocs: obsidian_vaults:/obsidian_vaults:ro.

Сервис в нашем стеке выглядит так:

 mkdocs: # Сборка документации (Obsidian vault -> статический HTML)
   image: squidfunk/mkdocs-material:latest # Последняя версия образа MkDocs Material
   container_name: mkdocs # Имя контейнера
   restart: unless-stopped # Автоперезапуск
   volumes:
      - obsidian_vaults:/obsidian_vaults:ro # Источник: vault'ы Obsidian — только чтение
      - html_notes:/html_output # Выход: собранный HTML (его отдаёт Caddy)
      - ./mkdocs:/docs # Конфиг/скрипты mkdocs (mkdocs.yml, entrypoint.sh и т.д.)
   environment:
      - OBSIDIAN_VAULTS_PATH=/obsidian_vaults # Путь к vault'ам внутри контейнера
      - MKDOCS_DOCS_PATH=/docs/docs # Путь к исходным markdown внутри /docs
      - MKDOCS_CONFIG_PATH=/docs/mkdocs.yml # Путь к конфигу MkDocs
   entrypoint: /docs/entrypoint.sh # Кастомный entrypoint: генерация/сборка сайта
   networks:
      - ollama-network # Общая сеть
   depends_on:
      - obsidian # Ждём vault'ы/контейнер (хотя том и так общий)
 

Всего в стеке 9 микросервисов, связанных сетью и защищенных TLS-сертификатами. Сам стек без запущенных моделей потребляет около 2 GB оперативной памяти и порядка 50 GB жесткого диска.

Запуск стека на VPS

Зайдите на VPS и выполните команду git clone https://github.com/Pseudolukian/docker_composes/, затем перейдите в директорию ollama_openwebui_n8n и выполните команду docker compose up -d. Если Docker Compose у вас еще не установлен — обратитесь к нашему разделу базы знаний про Docker, где есть подробные инструкции по установке Docker и описаны Docker-команды, нужные для запуска стека.

Проверить статус контейнеров можно с помощью команды docker compose ps --status running --format "table {{.Name}}\t{{.State}}" | head. Статус (STATE) всех контейнеров должен быть running:

Если какой-то из контейнеров не запустился — понять причину ошибки поможет команда docker logs . После успешного запуска стека и настройки DNS можно переходить в браузер и пробовать открыть, например, n8n.

Разбор n8n workflow

Ключевыми узлами работы нашего workflow являются Telegram-, HTTP Request- и IF-ноды, а сердцем и мозгом — Basic LLM Chain-нода. Графически workflow выглядит следующим образом:

Скачать workflow проекта в JSON-формате можно из этого репозитория или просто указать этот URL в пустом workflow для импорта.

Разберем свойства нод, которые используются в workflow, и сразу покажем, как они работают в нашем проекте.

HTTP Request-нода

С помощью HTTP Request-ноды n8n связывается по API с Obsidian (сохранение заметки, обновление TOC, проверка операции сохранения заметки) и с Python-сервисом article-parser (отправка ссылки от пользователя для очистки текста от HTML-тегов). HTTP Request-нода находится в основном разделе выбора нод:

Для работы ноды нужно обязательно заполнить следующие поля:

Для опций Send Query Parameters, Send Headers, Send Body можно выбрать один из двух форматов отправки тела параметра:

Выбор формата отправки параметров HTTP-запроса основывается на свойствах и спецификации API, на который отправляется запрос. Так, например, для отправки HTTP-запроса на извлечение текста статьи по ссылке в Python-микросервис article-parser используются следующие параметры HTTP Request-ноды:

{
"text":
"{{ $('Telegram Trigger').item.json.message.link_preview_options.url }}"
}

В приведённом примере HTTP Request-нода посылает POST-запрос на эндпоинт http://article-parser:8880/parse с телом запроса в формате JSON. Заголовки для аутентификации не передаются, так как этого не требует эндпоинт. Ниже — пример HTTP-запроса к API Obsidian, где уже передаются данные для аутентификации, как того требует спецификация API.

Пример отправки HTTP-запроса на добавление заметки в Obsidian с использованием HTTP Request-ноды:

{
"text": "{{ jsonStringify($('Reader').item.json.output.text) }}"
}

В этом примере есть два важных момента, на которые нужно обратить внимание:

  1. Первый важный момент — URL, который указывает на Caddy, а не на Obsidian. Сделано это специально для того, чтобы обеспечить TLS-защищенность соединения. Caddy шифрует трафик и проксирует запрос в API-эндпоинт Obsidian.
  2. Второй важный момент — значение поля Generic Auth Type, которое задано как Header auth. При таком типе аутентификации будут передаваться заголовки с данными для аутентификации, которые указаны в Header Auth account — заранее созданных и сохраненных в n8n данных для подключения.

HTTP Request-нода — мощный и гибкий строительный элемент автоматизации n8n. Она позволяет вынести часть бизнес-логики во внешние сервисы или использовать n8n как мост для связи микросервисов между собой по API.

IF-нода

IF-нода позволяет строить ветвление логики workflow, основанное на различных условиях: сравнение элементов, наличие или отсутствие искомого элемента в массиве, точное или частичное соответствие, а также другие проверки.

IF-нода находится в основном разделе выбора нод:

В нашем проекте IF-нода используется в основном для проверки наличия текста и URL в сообщении.

Basic LLM Chain-нода

Работа с AI — это одна из основных фишек n8n, и для работы с AI в n8n есть разные специализированные ноды, предназначенные для решения общих и конкретных задач:

Все перечисленные выше ноды находятся в основном разделе выбора нод:

В нашем workflow используется Basic LLM Chain для решения задачи чтения и создания summary. Мы выбрали Basic LLM Chain, а не Summarization Chain, по причине того, что Basic LLM Chain поддерживает возможность задать нужный формат вывода результата работы AI-агента. Для нашей задачи — это важный параметр, так как AI генерирует ещё и короткое название файла заметки для Obsidian на базе прочитанного материала.

Выходной формат данных в виде JSON выглядит так:

{
"note_name": "string",
"text": "string"
}

Где note_name — это название файла заметки на английском языке без пробелов и спецсимволов, а text — текст заметки, оформленный по следующему шаблону:

Так как формат вывода для нашего проекта критически важен, в связке с Basic LLM Chain мы применили ещё несколько специальных нод: Auto-fixing Output Parser и Structured Output Parser. Эти ноды нужны для строгого соблюдения выходного формата данных. Связка нод работает так: Basic LLM Chain формирует текст в соответствии с JSON-моделью, указанной в Structured Output Parser, а потом полученный JSON отправляется в ноду Auto-fixing Output Parser, которая подключена к AI так же, как и Basic LLM Chain. Там ответ модели ещё раз сравнивается с моделью в Structured Output Parser: если форма ответа совпадает с референсной моделью — результат работы AI идёт дальше по workflow, если же форма ответа не совпадает с заданной моделью — Auto-fixing Output Parser подгоняет ответ под заданную модель с помощью AI.

В workflow конструкция из Basic LLM chain, Auto-fixing Output Parser и Structured Output Parser выглядит следующим образом:

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

Telegram-нода

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

В проекте используются два вида Telegram-нод.

1. Telegram trigger on message-нода активирует workflow, когда пользователь отправляет сообщение в Telegram-бота. Находится нода в разделе Telegram → Triggers → On message.

У ноды Telegram trigger on message есть важное свойство — она умеет разбирать входящие сообщения от пользователя на составные части и в том числе определять URL. Нам не нужно находить ссылку на статью в тексте сообщения пользователя сторонними утилитами или JS-кодом — мы просто используем переменную {{ $json.message.link_preview_options.url }}: она хранит нужный нам URL.

2. Telegram: send a text message-нода доставки сообщения пользователю через Telegram-бот. Находится нода в разделе Telegram → Message actions → Send a text message.

Ноду Telegram: send a text message мы используем для отправки пользователю сообщений о статусе работы workflow и об ошибках, если они случаются. Сейчас workflow работает в режиме отладки, и любая ошибка приводит к завершению работы.

У Telegram-нод, например у Telegram: send a text message-ноды, есть поле Chat ID — это девятизначный уникальный номер, который позволяет ботам и сторонним приложениям отправлять сообщения конкретным пользователям, в группы или каналы через Telegram Bot API.

Поле Chat ID обязательно для заполнения. Значение поля можно получить из Telegram trigger on message-ноды. Например, с помощью такой конструкции: {{ $node["Telegram Trigger"].json.message.chat.id }}, где $node["Telegram Trigger"] — ссылка на ноду, а .json.message.chat.id — обращение к переменной, хранящей ID чата.

Запуск и тестирование workflow

После скачивания нашего шаблона workflow нужно заменить параметры подключения в Telegram trigger on message и Basic LLM Chain. В Telegram trigger on message замените значение в поле Credential to connect with на ваш API-ключ, а в Basic LLM Chain — модель на ту, которую вы используете в Ollama.

После того как необходимые настройки будут выставлены, workflow можно запускать в тестовом режиме, чтобы убедиться, что нет ошибок. Запустить workflow в отладочном режиме можно нажатием кнопки Test Workflow:

Если тестовый режим не выявил ошибок и workflow успешно отработал, его можно запускать в продакшн-режиме. Для этого достаточно установить переключатель Inactive в режим Active:

На этом работа с workflow завершена. Теперь можно переходить в Telegram и отправлять боту URL на статью.

С чего все начиналось и что получилось в итоге?

Перед нами стояла задача создать Telegram-бота, который будет получать сообщения, в которых есть URL на статьи, читать их, делать саммари и сохранять заметку по прочитанной статье в Obsidian и Mkdocs для большего удобства.

Бэкенд был реализован на микросервисах с помощью Docker. В контейнерах были запущены: n8n, Obsidian, MkDocs, Netdata, article-parser, Caddy, caddy-perms-fixer, Ollama, Open WebUI. Стек был запущен на VPS следующей конфигурации: 8 CPU-ядер, 16 GB DDR, 300 GB SSD. Использовалась локально запущенная LLM (deepseek-r1) через Ollama.

Также был настроен домен и были настроены DNS-записи для субдоменов. В качестве HTTP-сервера мы использовали Caddy, который нам еще из коробки обеспечил TLS-шифрование между сервисами.

Также был настроен домен и были настроены DNS-записи для субдоменов. В качестве HTTP-сервера мы использовали Caddy, который нам еще из коробки обеспечил TLS-шифрование между сервисами.

При разработке workflow мы вынесли часть бизнес-логики в отдельный микросервис article-parser, который очищает текст исходной статьи от лишнего для нас HTML-кода. Сам workflow можно либо скачать в виде JSON-файла из этого репозитория, либо импортировать workflow в пустой шаблон n8n.

В итоге получился удобный и практичный Telegram-бот, который экономит время, а также удалось сделать крепкую техническую базу для развития ваших собственных проектов на базе n8n. Хорошим дополнением к уже наработанному материалу и опыту будут ссылки на нашу базу знаний, где есть раздел про Docker.

И, конечно же, вы всегда можете найти ВМ нужной вам конфигурации в разделе VPS/VDS.