Ежедневная автоматическая загрузка xray-логов со всех нод в S3-хранилище, парсинг ошибок и красивый отчёт в Telegram. Полная инструкция.
Каждая нода ежедневно загружает свежие access.log и error.log от remnanode в S3-bucket. Раз в день collector проходит по bucket'у, формирует отчёт и шлёт в Telegram. Все логи остаются в S3 для расследований.
Без баз данных и Docker. Полностью на стандартных инструментах Linux.
/var/log/remnanode/logrotate — установщик заменяет дефолтный конфиг remnanode (size 50M, rotate 3) на daily-схему. Uploader забирает access.log.1 утром, gzip'ит и шлёт в S3.Можно использовать любого S3-совместимого провайдера. Я покажу на Timeweb.
Зайти в Timeweb Cloud → Хранилище S3 → Создать бакет:
vpn-logs)После создания у бакета сразу есть встроенные S3-ключи (Access Key + Secret Key) — найти их в свойствах созданного бакета.
S3 endpoint: https://s3.twcstorage.ru S3 bucket: YOUR_BUCKET_NAME Access Key: YOUR_ACCESS_KEY Secret Key: YOUR_SECRET_KEY
Нужны две вещи: токен бота и твой Telegram ID куда слать отчёты.
Через @BotFather создай бота — получишь токен.
Найди своего бота по username в Telegram → отправь ему /start. Без этого бот не сможет тебе писать.
Bot token: YOUR_BOT_TOKEN Chat ID: YOUR_CHAT_ID
Любой VPS с Debian 12+ / Ubuntu 22+. Желательно не в РФ (чтобы избежать блокировок Telegram API). Финский / эстонский — идеально.
SSH под root на collector-сервер и одной командой:
curl -fsSL https://logs.killu.net/install-collector.sh | bash
Скрипт спросит твои значения и сам всё настроит:
Файл /etc/log-collector/inventory.yaml — это список ожидаемых нод. По нему collector понимает «кто из 36 нод загрузил логи за сегодня, кого нет».
add-node.sh (Шаг 5). Он сам добавит запись в правильном формате.Если всё-таки хочется глянуть/поправить — открой файл редактором:
nano /etc/log-collector/inventory.yaml
Формат внутри:
nodes: - { country: DE, name: de-1, ip: NODE_IP_1 } - { country: NL, name: nl-1, ip: NODE_IP_2 } - { country: US, name: us-1, ip: NODE_IP_3 }
Три поля, всё что нужно:
| Поле | Что писать · откуда брать |
|---|---|
country |
Двухбуквенный код страны: DE, NL, US, RU, FR... Используется только для группировки в Telegram-отчёте. Не знаешь — ставь ??. |
name |
Короткое уникальное имя — de-1, nl-2, us-spb. Должно совпадать с тем именем, которое ты введёшь при установке uploader'а на самой ноде (от него зависит путь в S3). |
ip |
Публичный IP ноды — тот же что в панели Remnawave или у хостера. По нему collector проверяет «загрузилась ли эта нода». |
Опционально: можно добавить поле role со значением routing или vpn-wl если у тебя есть роутинговые/whitelist-ноды. По умолчанию все ноды считаются обычными VPN — это работает для большинства случаев.
systemctl status log-report.timer journalctl -u log-report -n 50 → в Telegram придёт тестовый отчёт
Запускается на каждой ноде где стоит remnanode. После установки нода каждый день в 01:00 UTC отправляет вчерашние логи в твой S3.
/var/log/remnanode/. По умолчанию remnanode пишет их внутрь docker-контейнера, и uploader их там не достанет.ls /var/log/remnanode/ — должны быть access.log / error.log.Открой docker-compose.yml твоего remnanode (обычно /opt/remnanode/docker-compose.yml) и добавь volume:
services:
remnanode:
image: remnawave/node:latest
...
volumes:
- /var/log/remnanode:/var/log/remnanode ← добавь эту строку
Дальше в Remnawave-панели в конфиге xray для этой ноды убедись что пути логов правильные:
"log": {
"access": "/var/log/remnanode/access.log",
"error": "/var/log/remnanode/error.log",
"loglevel": "warning"
}
Применить — пересоздать контейнер:
cd /opt/remnanode docker compose up -d
После любой активности на ноде в /var/log/remnanode/ появятся файлы. Можно идти к 4.1.
Скачай install-uploader.sh к себе, открой и впиши свои S3-параметры в самом верху файла. Потом просто заливай его на каждую новую ноду — интерактивно будет спрашивать только имя.
S3_BUCKET="YOUR_BUCKET_ID" S3_ENDPOINT="https://s3.twcstorage.ru" S3_ACCESS_KEY="YOUR_ACCESS_KEY" S3_SECRET_KEY="YOUR_SECRET_KEY" NODE_NAME="" UPLOAD_TIME="01:00"
Дальше — заливаешь на ноду как обычно (scp + ssh) и запускаешь. Скрипт спросит имя ноды (например de-1, nl-2) — это уникальное короткое имя. В S3 финальный путь собирается автоматически как {имя}__{ip-ноды}/, IP подтягивается через api.ipify.org. Имя должно совпадать с тем что ты добавил в inventory.yaml на collector'е.
Если хочешь передать имя сразу без интерактива — добавь флаг --node-name=de-5 к команде запуска.
gzip, curl, mc (MinIO client)/etc/log-upload/secrets.env с твоими S3-ключами (chmod 600)/usr/local/sbin/log-upload.sh/etc/logrotate.d/remnanode на daily-ротацию (старый бэкапится в .bak_*). Дефолтный конфиг от remnanode (size 50M, rotate 3) на нагруженных нодах перетирает вчерашние логи до того как uploader успеет их забрать.Скрипт не трогает:
/var/log/remnanode/*.log — не удаляет, не редактирует, только читает/var/log/remnanode/*.log {
daily
rotate 7
compress
delaycompress
copytruncate
missingok
notifempty
su root root
}
Два шага: добавить ноду в inventory на collector'е, потом поставить uploader на саму ноду.
curl -fsSL https://logs.killu.net/add-node.sh | bash -s -- \
--name=de-5 \
--ip=NODE_IP
Скрипт проверит что ноды ещё нет, добавит запись в /etc/log-collector/inventory.yaml. В конце выведет готовую команду для следующего шага.
Зайди на ноду по обычному SSH и запусти install-uploader.sh (как в Шаге 4). Команду со всеми правильными ключами тебе подсказал add-node.sh в предыдущем шаге.
# статус таймера systemctl status log-upload.timer # ручной тестовый запуск /usr/local/sbin/log-upload.sh # лог скрипта tail -30 /var/log/log-upload.log
В /var/log/log-upload.log должны быть строки:
[2026-05-21T01:00:12Z] ═══ START upload for de-1@NODE_IP ═══ [2026-05-21T01:00:13Z] picked /var/log/remnanode/access.log.1.gz (already gz) [2026-05-21T01:00:14Z] uploaded → 2026/05/20/de-1__NODE_IP/xray-access.log.gz [2026-05-21T01:00:15Z] uploaded → 2026/05/20/de-1__NODE_IP/_meta.json [2026-05-21T01:00:15Z] ═══ DONE ok=5 fail=0 ═══
# посмотреть S3 mc ls -r tw/YOUR_BUCKET/ # ручной отчёт (придёт в Telegram) /usr/local/bin/log-report.py
Открой бакет в web-UI Timeweb. Должна быть структура:
2026/
└── 05/
└── 20/
├── de-1__NODE_IP/
│ ├── xray-access.log.gz
│ ├── xray-error.log.gz
│ ├── fail2ban.log.gz
│ └── _meta.json
├── de-2__NODE_IP/
│ └── ...
На следующее утро (06:00 UTC = 09:00 МСК) придёт отчёт автоматически:
Состоит из двух сообщений:
report-YYYY-MM-DD.txt.gz — детальная разбивка| Время UTC | МСК | Что происходит |
|---|---|---|
00:00 | 03:00 | cron.daily запускает logrotate: access.log → access.log.1 (несжатый из-за delaycompress) |
01:00 | 04:00 | На каждой ноде запускается uploader (с random delay до 10 мин) |
~01:05 | 04:05 | Все ноды обычно закончили загрузку |
06:00 | 09:00 | Collector формирует отчёт и шлёт в Telegram |
| Путь | Что |
|---|---|
/usr/local/sbin/log-upload.sh | основной скрипт загрузки |
/etc/log-upload/secrets.env | S3 credentials (chmod 600) |
/etc/systemd/system/log-upload.timer | расписание (01:00 UTC) |
/var/log/log-upload.log | лог работы скрипта |
| Путь | Что |
|---|---|
/usr/local/bin/log-report.py | скрипт отчёта |
/etc/log-collector/inventory.yaml | список нод |
/etc/log-collector/secrets.env | S3 + Telegram креды |
/etc/systemd/system/log-report.timer | расписание (06:00 UTC) |
/var/log/log-collector/ | сохранённые отчёты |
Что качать, когда и куда. Каждый файл с описанием назначения.
| № | Файл | Где запускать | Что делает |
|---|---|---|---|
| 1 | install-collector.sh | один раз на collector-сервере |
Ставит на пустой VPS всё необходимое: Python, mc, fail2ban, UFW. Создаёт systemd-timer на ежедневный отчёт. Шлёт тестовое сообщение в Telegram. |
| 2 | install-uploader.sh | на каждой VPN-ноде |
Ставит zstd, mc. Создаёт systemd-timer (01:00 UTC ежедневно), который загружает access.log.1.gz от remnanode в S3. |
| 3 | add-node.sh | на collector'е (когда купил новую ноду) |
Добавляет ноду в inventory.yaml. После этого зайди на саму ноду по SSH и поставь там uploader (файл #2). |
| — | log-upload.sh | автоматически лежит на ноде |
Рабочий скрипт ежедневной загрузки. Ставится автоматом через install-uploader.sh. Скачивать вручную не нужно. |
| — | log-report.py | автоматически на collector'е |
Рабочий скрипт отчёта. Ставится через install-collector.sh. Скачивать вручную не нужно. |